@azure/eventhubs-checkpointstore-table 1.0.0-alpha.20211213.3 → 1.0.0-alpha.20220128.3
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/dist/index.js +10 -10
- package/dist/index.js.map +1 -1
- package/dist-esm/src/tableCheckpointStore.js +10 -10
- package/dist-esm/src/tableCheckpointStore.js.map +1 -1
- package/package.json +10 -13
package/dist/index.js
CHANGED
|
@@ -57,7 +57,7 @@ class TableCheckpointStore {
|
|
|
57
57
|
const partitionKey = `${fullyQualifiedNamespace} ${eventHubName} ${consumerGroup} Ownership`;
|
|
58
58
|
const partitionOwnershipArray = [];
|
|
59
59
|
const entitiesIter = this._tableClient.listEntities({
|
|
60
|
-
queryOptions: { filter: dataTables.odata `PartitionKey eq ${partitionKey}` }
|
|
60
|
+
queryOptions: { filter: dataTables.odata `PartitionKey eq ${partitionKey}` },
|
|
61
61
|
});
|
|
62
62
|
try {
|
|
63
63
|
try {
|
|
@@ -73,7 +73,7 @@ class TableCheckpointStore {
|
|
|
73
73
|
ownerId: entity.ownerid,
|
|
74
74
|
partitionId: entity.rowKey,
|
|
75
75
|
lastModifiedTimeInMs: new Date(entity.timestamp).getTime(),
|
|
76
|
-
etag: entity.etag
|
|
76
|
+
etag: entity.etag,
|
|
77
77
|
};
|
|
78
78
|
partitionOwnershipArray.push(partitionOwnership);
|
|
79
79
|
}
|
|
@@ -113,14 +113,14 @@ class TableCheckpointStore {
|
|
|
113
113
|
const ownershipEntity = {
|
|
114
114
|
partitionKey: partitionKey,
|
|
115
115
|
rowKey: ownership.partitionId,
|
|
116
|
-
ownerid: ownership.ownerId
|
|
116
|
+
ownerid: ownership.ownerId,
|
|
117
117
|
};
|
|
118
118
|
// When we have an etag, we know the entity existed.
|
|
119
119
|
// If we encounter an error we should fail.
|
|
120
120
|
try {
|
|
121
121
|
if (ownership.etag) {
|
|
122
122
|
const updatedMetadata = await this._tableClient.updateEntity(ownershipEntity, "Replace", {
|
|
123
|
-
etag: ownership.etag
|
|
123
|
+
etag: ownership.etag,
|
|
124
124
|
});
|
|
125
125
|
const entityRetrieved = await this._tableClient.getEntity(ownershipEntity.partitionKey, ownershipEntity.rowKey);
|
|
126
126
|
if (!entityRetrieved.timestamp) {
|
|
@@ -135,9 +135,9 @@ class TableCheckpointStore {
|
|
|
135
135
|
const newOwnershipMetadata = await this._tableClient.createEntity(ownershipEntity, {
|
|
136
136
|
requestOptions: {
|
|
137
137
|
customHeaders: {
|
|
138
|
-
Prefer: "return-content"
|
|
139
|
-
}
|
|
140
|
-
}
|
|
138
|
+
Prefer: "return-content",
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
141
|
});
|
|
142
142
|
if (!_hasTimestamp(newOwnershipMetadata)) {
|
|
143
143
|
throw new Error(`Unable to retrieve timestamp from partitionKey "${partitionKey}", rowKey "${ownershipEntity.rowKey}"`);
|
|
@@ -177,7 +177,7 @@ class TableCheckpointStore {
|
|
|
177
177
|
const partitionKey = `${fullyQualifiedNamespace} ${eventHubName} ${consumerGroup} Checkpoint`;
|
|
178
178
|
const checkpoints = [];
|
|
179
179
|
const entitiesIter = this._tableClient.listEntities({
|
|
180
|
-
queryOptions: { filter: dataTables.odata `PartitionKey eq ${partitionKey}` }
|
|
180
|
+
queryOptions: { filter: dataTables.odata `PartitionKey eq ${partitionKey}` },
|
|
181
181
|
});
|
|
182
182
|
try {
|
|
183
183
|
for (var entitiesIter_2 = tslib.__asyncValues(entitiesIter), entitiesIter_2_1; entitiesIter_2_1 = await entitiesIter_2.next(), !entitiesIter_2_1.done;) {
|
|
@@ -188,7 +188,7 @@ class TableCheckpointStore {
|
|
|
188
188
|
fullyQualifiedNamespace,
|
|
189
189
|
partitionId: entity.rowKey,
|
|
190
190
|
offset: parseInt(entity.offset, 10),
|
|
191
|
-
sequenceNumber: parseInt(entity.sequencenumber, 10)
|
|
191
|
+
sequenceNumber: parseInt(entity.sequencenumber, 10),
|
|
192
192
|
});
|
|
193
193
|
}
|
|
194
194
|
}
|
|
@@ -216,7 +216,7 @@ class TableCheckpointStore {
|
|
|
216
216
|
partitionKey: partitionKey,
|
|
217
217
|
rowKey: checkpoint.partitionId,
|
|
218
218
|
sequencenumber: checkpoint.sequenceNumber.toString(),
|
|
219
|
-
offset: checkpoint.offset.toString()
|
|
219
|
+
offset: checkpoint.offset.toString(),
|
|
220
220
|
};
|
|
221
221
|
try {
|
|
222
222
|
await this._tableClient.upsertEntity(checkpointEntity);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/log.ts","../src/tableCheckpointStore.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { createClientLogger } from \"@azure/logger\";\n\n/**\n * The `@azure/logger` configuration for this package.\n * This will output logs using the `azure:eventhubs-checkpointstore-table` namespace prefix.\n */\nexport const logger = createClientLogger(\"eventhubs-checkpointstore-table\");\n\n/**\n * Logs the error's stack trace to \"verbose\" if a stack trace is available.\n * @param error - Error containing a stack trace.\n * @internal\n */\nexport function logErrorStackTrace(error: unknown): void {\n if (error && typeof error === \"object\" && \"stack\" in error) {\n logger.verbose((error as any).stack);\n }\n}\n","// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { Checkpoint, CheckpointStore, PartitionOwnership } from \"@azure/event-hubs\";\nimport { TableClient, TableInsertEntityHeaders, odata } from \"@azure/data-tables\";\nimport { logErrorStackTrace, logger } from \"./log\";\n\n/**\n *\n * Checks if the value contains a `Timestamp` field of type `string`.\n */\nfunction _hasTimestamp<T extends TableInsertEntityHeaders>(\n value: T\n): value is T & { Timestamp: string } {\n return typeof (value as any).Timestamp === \"string\";\n}\n\n/**\n * A checkpoint entity of type CheckpointEntity to be stored in the table\n * @internal\n *\n */\nexport interface CheckpointEntity {\n /**\n * The partitionKey is a composite key assembled in the following format:\n * `${fullyQualifiedNamespace} ${eventHubName} ${consumerGroup} Checkpoint`\n */\n partitionKey: string;\n /**\n * The rowKey is the partitionId\n *\n */\n rowKey: string;\n sequencenumber: string;\n offset: string;\n}\n\n/**\n * An ownership entity of type PartitionOwnership to be stored in the table\n * @internal\n * @hidden\n */\nexport interface PartitionOwnershipEntity {\n /**\n * The partitionKey is a composite key assembled in the following format:\n * `${fullyQualifiedNamespace} ${eventHubName} ${consumerGroup} Ownership`\n */\n partitionKey: string;\n /**\n * The rowKey is the partitionId\n *\n */\n rowKey: string;\n ownerid: string;\n}\n\n/**\n * An implementation of CheckpointStore that uses Azure Table Storage to persist checkpoint data.\n */\nexport class TableCheckpointStore implements CheckpointStore {\n private _tableClient: TableClient;\n\n constructor(tableClient: TableClient) {\n this._tableClient = tableClient;\n }\n\n /**\n * Get the list of all existing partition ownership from the underlying data store. May return empty\n * results if there are is no existing ownership information.\n * Partition Ownership contains the information on which `EventHubConsumerClient` subscribe call is currently processing the partition.\n *\n * @param fullyQualifiedNamespace - The fully qualified Event Hubs namespace. This is likely to be similar to\n * <yournamespace>.servicebus.windows.net.\n * @param eventHubName - The event hub name.\n * @param consumerGroup - The consumer group name.\n * @param options - A set of options that can be specified to influence the behavior of this method.\n * - `abortSignal`: A signal used to request operation cancellation.\n * - `tracingOptions`: Options for configuring tracing.\n * @returns Partition ownership details of all the partitions that have had an owner.\n */\n async listOwnership(\n fullyQualifiedNamespace: string,\n eventHubName: string,\n consumerGroup: string\n ): Promise<PartitionOwnership[]> {\n const partitionKey = `${fullyQualifiedNamespace} ${eventHubName} ${consumerGroup} Ownership`;\n const partitionOwnershipArray: PartitionOwnership[] = [];\n const entitiesIter = this._tableClient.listEntities<PartitionOwnershipEntity>({\n queryOptions: { filter: odata`PartitionKey eq ${partitionKey}` }\n });\n try {\n for await (const entity of entitiesIter) {\n if (!entity.timestamp) {\n throw new Error(\n `Unable to retrieve timestamp from partitionKey \"${partitionKey}\", rowKey \"${entity.rowKey}\"`\n );\n }\n\n const partitionOwnership: PartitionOwnership = {\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup,\n ownerId: entity.ownerid,\n partitionId: entity.rowKey,\n lastModifiedTimeInMs: new Date(entity.timestamp).getTime(),\n etag: entity.etag\n };\n partitionOwnershipArray.push(partitionOwnership);\n }\n return partitionOwnershipArray;\n } catch (err) {\n logger.warning(`Error occurred while fetching the list of entities`, err.message);\n logErrorStackTrace(err);\n if (err?.name === \"AbortError\") throw err;\n\n throw new Error(`Error occurred while fetching the list of entities. \\n${err}`);\n }\n }\n\n /**\n * Claim ownership of a list of partitions. This will return the list of partitions that were\n * successfully claimed.\n *\n * @param partitionOwnership - The list of partition ownership this instance is claiming to own.\n * @param options - A set of options that can be specified to influence the behavior of this method.\n * - `abortSignal`: A signal used to request operation cancellation.\n * - `tracingOptions`: Options for configuring tracing.\n * @returns A list partitions this instance successfully claimed ownership.\n */\n async claimOwnership(partitionOwnership: PartitionOwnership[]): Promise<PartitionOwnership[]> {\n const partitionOwnershipArray: PartitionOwnership[] = [];\n\n for (const ownership of partitionOwnership) {\n const updatedOwnership = { ...ownership };\n const partitionKey = `${ownership.fullyQualifiedNamespace} ${ownership.eventHubName} ${ownership.consumerGroup} Ownership`;\n const ownershipEntity: PartitionOwnershipEntity = {\n partitionKey: partitionKey,\n rowKey: ownership.partitionId,\n ownerid: ownership.ownerId\n };\n\n // When we have an etag, we know the entity existed.\n // If we encounter an error we should fail.\n try {\n if (ownership.etag) {\n const updatedMetadata = await this._tableClient.updateEntity(ownershipEntity, \"Replace\", {\n etag: ownership.etag\n });\n const entityRetrieved = await this._tableClient.getEntity(\n ownershipEntity.partitionKey,\n ownershipEntity.rowKey\n );\n if (!entityRetrieved.timestamp) {\n throw new Error(\n `Unable to retrieve timestamp from partitionKey \"${partitionKey}\", rowKey \"${entityRetrieved.rowKey}\"`\n );\n }\n updatedOwnership.lastModifiedTimeInMs = new Date(entityRetrieved.timestamp).getTime();\n updatedOwnership.etag = updatedMetadata.etag;\n partitionOwnershipArray.push(updatedOwnership);\n logger.info(\n `[${ownership.ownerId}] Claimed ownership successfully for partition: ${ownership.partitionId}`,\n `LastModifiedTime: ${ownership.lastModifiedTimeInMs}, ETag: ${ownership.etag}`\n );\n } else {\n const newOwnershipMetadata = await this._tableClient.createEntity(ownershipEntity, {\n requestOptions: {\n customHeaders: {\n Prefer: \"return-content\"\n }\n }\n });\n\n if (!_hasTimestamp(newOwnershipMetadata)) {\n throw new Error(\n `Unable to retrieve timestamp from partitionKey \"${partitionKey}\", rowKey \"${ownershipEntity.rowKey}\"`\n );\n }\n\n updatedOwnership.lastModifiedTimeInMs = new Date(\n newOwnershipMetadata.Timestamp\n ).getTime();\n updatedOwnership.etag = newOwnershipMetadata.etag;\n partitionOwnershipArray.push(updatedOwnership);\n }\n } catch (err) {\n if (err.statusCode === 412) {\n // etag failures (precondition not met) aren't fatal errors. They happen\n // as multiple consumers attempt to claim the same partition (first one wins)\n // and losers get this error.\n logger.verbose(\n `[${ownership.ownerId}] Did not claim partition ${ownership.partitionId}. Another processor has already claimed it.`\n );\n continue;\n }\n logger.warning(\n `Error occurred while claiming ownership for partition: ${ownership.partitionId}`,\n err.message\n );\n logErrorStackTrace(err);\n }\n }\n return partitionOwnershipArray;\n }\n\n /**\n * Lists all the checkpoints in a data store for a given namespace, eventhub and consumer group.\n *\n * @param fullyQualifiedNamespace - The fully qualified Event Hubs namespace. This is likely to be similar to\n * <yournamespace>.servicebus.windows.net.\n * @param eventHubName - The event hub name.\n * @param consumerGroup - The consumer group name.\n * @param options - A set of options that can be specified to influence the behavior of this method.\n * - `abortSignal`: A signal used to request operation cancellation.\n * - `tracingOptions`: Options for configuring tracing.\n */\n async listCheckpoints(\n fullyQualifiedNamespace: string,\n eventHubName: string,\n consumerGroup: string\n ): Promise<Checkpoint[]> {\n const partitionKey = `${fullyQualifiedNamespace} ${eventHubName} ${consumerGroup} Checkpoint`;\n const checkpoints: Checkpoint[] = [];\n const entitiesIter = this._tableClient.listEntities<CheckpointEntity>({\n queryOptions: { filter: odata`PartitionKey eq ${partitionKey}` }\n });\n for await (const entity of entitiesIter) {\n checkpoints.push({\n consumerGroup,\n eventHubName,\n fullyQualifiedNamespace,\n partitionId: entity.rowKey,\n offset: parseInt(entity.offset, 10),\n sequenceNumber: parseInt(entity.sequencenumber, 10)\n });\n }\n return checkpoints;\n }\n\n /**\n * Updates the checkpoint in the data store for a partition.\n *\n * @param checkpoint - The checkpoint.\n * @param options - A set of options that can be specified to influence the behavior of this method.\n * - `abortSignal`: A signal used to request operation cancellation.\n * - `tracingOptions`: Options for configuring tracing.\n * @returns A promise that resolves when the checkpoint has been updated.\n */\n async updateCheckpoint(checkpoint: Checkpoint): Promise<void> {\n const partitionKey = `${checkpoint.fullyQualifiedNamespace} ${checkpoint.eventHubName} ${checkpoint.consumerGroup} Checkpoint`;\n const checkpointEntity: CheckpointEntity = {\n partitionKey: partitionKey,\n rowKey: checkpoint.partitionId,\n sequencenumber: checkpoint.sequenceNumber.toString(),\n offset: checkpoint.offset.toString()\n };\n try {\n await this._tableClient.upsertEntity(checkpointEntity);\n logger.verbose(`Updated checkpoint successfully for partition: ${checkpoint.partitionId}`);\n return;\n } catch (err) {\n logger.verbose(\n `Error occurred while upating the checkpoint for partition: ${checkpoint.partitionId}.`,\n err.message\n );\n throw err;\n }\n }\n}\n"],"names":["createClientLogger","odata","__asyncValues"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/log.ts","../src/tableCheckpointStore.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { createClientLogger } from \"@azure/logger\";\n\n/**\n * The `@azure/logger` configuration for this package.\n * This will output logs using the `azure:eventhubs-checkpointstore-table` namespace prefix.\n */\nexport const logger = createClientLogger(\"eventhubs-checkpointstore-table\");\n\n/**\n * Logs the error's stack trace to \"verbose\" if a stack trace is available.\n * @param error - Error containing a stack trace.\n * @internal\n */\nexport function logErrorStackTrace(error: unknown): void {\n if (error && typeof error === \"object\" && \"stack\" in error) {\n logger.verbose((error as any).stack);\n }\n}\n","// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { Checkpoint, CheckpointStore, PartitionOwnership } from \"@azure/event-hubs\";\nimport { TableClient, TableInsertEntityHeaders, odata } from \"@azure/data-tables\";\nimport { logErrorStackTrace, logger } from \"./log\";\n\n/**\n *\n * Checks if the value contains a `Timestamp` field of type `string`.\n */\nfunction _hasTimestamp<T extends TableInsertEntityHeaders>(\n value: T\n): value is T & { Timestamp: string } {\n return typeof (value as any).Timestamp === \"string\";\n}\n\n/**\n * A checkpoint entity of type CheckpointEntity to be stored in the table\n * @internal\n *\n */\nexport interface CheckpointEntity {\n /**\n * The partitionKey is a composite key assembled in the following format:\n * `${fullyQualifiedNamespace} ${eventHubName} ${consumerGroup} Checkpoint`\n */\n partitionKey: string;\n /**\n * The rowKey is the partitionId\n *\n */\n rowKey: string;\n sequencenumber: string;\n offset: string;\n}\n\n/**\n * An ownership entity of type PartitionOwnership to be stored in the table\n * @internal\n */\nexport interface PartitionOwnershipEntity {\n /**\n * The partitionKey is a composite key assembled in the following format:\n * `${fullyQualifiedNamespace} ${eventHubName} ${consumerGroup} Ownership`\n */\n partitionKey: string;\n /**\n * The rowKey is the partitionId\n *\n */\n rowKey: string;\n ownerid: string;\n}\n\n/**\n * An implementation of CheckpointStore that uses Azure Table Storage to persist checkpoint data.\n */\nexport class TableCheckpointStore implements CheckpointStore {\n private _tableClient: TableClient;\n\n constructor(tableClient: TableClient) {\n this._tableClient = tableClient;\n }\n\n /**\n * Get the list of all existing partition ownership from the underlying data store. May return empty\n * results if there are is no existing ownership information.\n * Partition Ownership contains the information on which `EventHubConsumerClient` subscribe call is currently processing the partition.\n *\n * @param fullyQualifiedNamespace - The fully qualified Event Hubs namespace. This is likely to be similar to\n * <yournamespace>.servicebus.windows.net.\n * @param eventHubName - The event hub name.\n * @param consumerGroup - The consumer group name.\n * @param options - A set of options that can be specified to influence the behavior of this method.\n * - `abortSignal`: A signal used to request operation cancellation.\n * - `tracingOptions`: Options for configuring tracing.\n * @returns Partition ownership details of all the partitions that have had an owner.\n */\n async listOwnership(\n fullyQualifiedNamespace: string,\n eventHubName: string,\n consumerGroup: string\n ): Promise<PartitionOwnership[]> {\n const partitionKey = `${fullyQualifiedNamespace} ${eventHubName} ${consumerGroup} Ownership`;\n const partitionOwnershipArray: PartitionOwnership[] = [];\n const entitiesIter = this._tableClient.listEntities<PartitionOwnershipEntity>({\n queryOptions: { filter: odata`PartitionKey eq ${partitionKey}` },\n });\n try {\n for await (const entity of entitiesIter) {\n if (!entity.timestamp) {\n throw new Error(\n `Unable to retrieve timestamp from partitionKey \"${partitionKey}\", rowKey \"${entity.rowKey}\"`\n );\n }\n\n const partitionOwnership: PartitionOwnership = {\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup,\n ownerId: entity.ownerid,\n partitionId: entity.rowKey,\n lastModifiedTimeInMs: new Date(entity.timestamp).getTime(),\n etag: entity.etag,\n };\n partitionOwnershipArray.push(partitionOwnership);\n }\n return partitionOwnershipArray;\n } catch (err) {\n logger.warning(`Error occurred while fetching the list of entities`, err.message);\n logErrorStackTrace(err);\n if (err?.name === \"AbortError\") throw err;\n\n throw new Error(`Error occurred while fetching the list of entities. \\n${err}`);\n }\n }\n\n /**\n * Claim ownership of a list of partitions. This will return the list of partitions that were\n * successfully claimed.\n *\n * @param partitionOwnership - The list of partition ownership this instance is claiming to own.\n * @param options - A set of options that can be specified to influence the behavior of this method.\n * - `abortSignal`: A signal used to request operation cancellation.\n * - `tracingOptions`: Options for configuring tracing.\n * @returns A list partitions this instance successfully claimed ownership.\n */\n async claimOwnership(partitionOwnership: PartitionOwnership[]): Promise<PartitionOwnership[]> {\n const partitionOwnershipArray: PartitionOwnership[] = [];\n\n for (const ownership of partitionOwnership) {\n const updatedOwnership = { ...ownership };\n const partitionKey = `${ownership.fullyQualifiedNamespace} ${ownership.eventHubName} ${ownership.consumerGroup} Ownership`;\n const ownershipEntity: PartitionOwnershipEntity = {\n partitionKey: partitionKey,\n rowKey: ownership.partitionId,\n ownerid: ownership.ownerId,\n };\n\n // When we have an etag, we know the entity existed.\n // If we encounter an error we should fail.\n try {\n if (ownership.etag) {\n const updatedMetadata = await this._tableClient.updateEntity(ownershipEntity, \"Replace\", {\n etag: ownership.etag,\n });\n const entityRetrieved = await this._tableClient.getEntity(\n ownershipEntity.partitionKey,\n ownershipEntity.rowKey\n );\n if (!entityRetrieved.timestamp) {\n throw new Error(\n `Unable to retrieve timestamp from partitionKey \"${partitionKey}\", rowKey \"${entityRetrieved.rowKey}\"`\n );\n }\n updatedOwnership.lastModifiedTimeInMs = new Date(entityRetrieved.timestamp).getTime();\n updatedOwnership.etag = updatedMetadata.etag;\n partitionOwnershipArray.push(updatedOwnership);\n logger.info(\n `[${ownership.ownerId}] Claimed ownership successfully for partition: ${ownership.partitionId}`,\n `LastModifiedTime: ${ownership.lastModifiedTimeInMs}, ETag: ${ownership.etag}`\n );\n } else {\n const newOwnershipMetadata = await this._tableClient.createEntity(ownershipEntity, {\n requestOptions: {\n customHeaders: {\n Prefer: \"return-content\",\n },\n },\n });\n\n if (!_hasTimestamp(newOwnershipMetadata)) {\n throw new Error(\n `Unable to retrieve timestamp from partitionKey \"${partitionKey}\", rowKey \"${ownershipEntity.rowKey}\"`\n );\n }\n\n updatedOwnership.lastModifiedTimeInMs = new Date(\n newOwnershipMetadata.Timestamp\n ).getTime();\n updatedOwnership.etag = newOwnershipMetadata.etag;\n partitionOwnershipArray.push(updatedOwnership);\n }\n } catch (err) {\n if (err.statusCode === 412) {\n // etag failures (precondition not met) aren't fatal errors. They happen\n // as multiple consumers attempt to claim the same partition (first one wins)\n // and losers get this error.\n logger.verbose(\n `[${ownership.ownerId}] Did not claim partition ${ownership.partitionId}. Another processor has already claimed it.`\n );\n continue;\n }\n logger.warning(\n `Error occurred while claiming ownership for partition: ${ownership.partitionId}`,\n err.message\n );\n logErrorStackTrace(err);\n }\n }\n return partitionOwnershipArray;\n }\n\n /**\n * Lists all the checkpoints in a data store for a given namespace, eventhub and consumer group.\n *\n * @param fullyQualifiedNamespace - The fully qualified Event Hubs namespace. This is likely to be similar to\n * <yournamespace>.servicebus.windows.net.\n * @param eventHubName - The event hub name.\n * @param consumerGroup - The consumer group name.\n * @param options - A set of options that can be specified to influence the behavior of this method.\n * - `abortSignal`: A signal used to request operation cancellation.\n * - `tracingOptions`: Options for configuring tracing.\n */\n async listCheckpoints(\n fullyQualifiedNamespace: string,\n eventHubName: string,\n consumerGroup: string\n ): Promise<Checkpoint[]> {\n const partitionKey = `${fullyQualifiedNamespace} ${eventHubName} ${consumerGroup} Checkpoint`;\n const checkpoints: Checkpoint[] = [];\n const entitiesIter = this._tableClient.listEntities<CheckpointEntity>({\n queryOptions: { filter: odata`PartitionKey eq ${partitionKey}` },\n });\n for await (const entity of entitiesIter) {\n checkpoints.push({\n consumerGroup,\n eventHubName,\n fullyQualifiedNamespace,\n partitionId: entity.rowKey,\n offset: parseInt(entity.offset, 10),\n sequenceNumber: parseInt(entity.sequencenumber, 10),\n });\n }\n return checkpoints;\n }\n\n /**\n * Updates the checkpoint in the data store for a partition.\n *\n * @param checkpoint - The checkpoint.\n * @param options - A set of options that can be specified to influence the behavior of this method.\n * - `abortSignal`: A signal used to request operation cancellation.\n * - `tracingOptions`: Options for configuring tracing.\n * @returns A promise that resolves when the checkpoint has been updated.\n */\n async updateCheckpoint(checkpoint: Checkpoint): Promise<void> {\n const partitionKey = `${checkpoint.fullyQualifiedNamespace} ${checkpoint.eventHubName} ${checkpoint.consumerGroup} Checkpoint`;\n const checkpointEntity: CheckpointEntity = {\n partitionKey: partitionKey,\n rowKey: checkpoint.partitionId,\n sequencenumber: checkpoint.sequenceNumber.toString(),\n offset: checkpoint.offset.toString(),\n };\n try {\n await this._tableClient.upsertEntity(checkpointEntity);\n logger.verbose(`Updated checkpoint successfully for partition: ${checkpoint.partitionId}`);\n return;\n } catch (err) {\n logger.verbose(\n `Error occurred while upating the checkpoint for partition: ${checkpoint.partitionId}.`,\n err.message\n );\n throw err;\n }\n }\n}\n"],"names":["createClientLogger","odata","__asyncValues"],"mappings":"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"}
|
|
@@ -36,7 +36,7 @@ export class TableCheckpointStore {
|
|
|
36
36
|
const partitionKey = `${fullyQualifiedNamespace} ${eventHubName} ${consumerGroup} Ownership`;
|
|
37
37
|
const partitionOwnershipArray = [];
|
|
38
38
|
const entitiesIter = this._tableClient.listEntities({
|
|
39
|
-
queryOptions: { filter: odata `PartitionKey eq ${partitionKey}` }
|
|
39
|
+
queryOptions: { filter: odata `PartitionKey eq ${partitionKey}` },
|
|
40
40
|
});
|
|
41
41
|
try {
|
|
42
42
|
try {
|
|
@@ -52,7 +52,7 @@ export class TableCheckpointStore {
|
|
|
52
52
|
ownerId: entity.ownerid,
|
|
53
53
|
partitionId: entity.rowKey,
|
|
54
54
|
lastModifiedTimeInMs: new Date(entity.timestamp).getTime(),
|
|
55
|
-
etag: entity.etag
|
|
55
|
+
etag: entity.etag,
|
|
56
56
|
};
|
|
57
57
|
partitionOwnershipArray.push(partitionOwnership);
|
|
58
58
|
}
|
|
@@ -92,14 +92,14 @@ export class TableCheckpointStore {
|
|
|
92
92
|
const ownershipEntity = {
|
|
93
93
|
partitionKey: partitionKey,
|
|
94
94
|
rowKey: ownership.partitionId,
|
|
95
|
-
ownerid: ownership.ownerId
|
|
95
|
+
ownerid: ownership.ownerId,
|
|
96
96
|
};
|
|
97
97
|
// When we have an etag, we know the entity existed.
|
|
98
98
|
// If we encounter an error we should fail.
|
|
99
99
|
try {
|
|
100
100
|
if (ownership.etag) {
|
|
101
101
|
const updatedMetadata = await this._tableClient.updateEntity(ownershipEntity, "Replace", {
|
|
102
|
-
etag: ownership.etag
|
|
102
|
+
etag: ownership.etag,
|
|
103
103
|
});
|
|
104
104
|
const entityRetrieved = await this._tableClient.getEntity(ownershipEntity.partitionKey, ownershipEntity.rowKey);
|
|
105
105
|
if (!entityRetrieved.timestamp) {
|
|
@@ -114,9 +114,9 @@ export class TableCheckpointStore {
|
|
|
114
114
|
const newOwnershipMetadata = await this._tableClient.createEntity(ownershipEntity, {
|
|
115
115
|
requestOptions: {
|
|
116
116
|
customHeaders: {
|
|
117
|
-
Prefer: "return-content"
|
|
118
|
-
}
|
|
119
|
-
}
|
|
117
|
+
Prefer: "return-content",
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
120
|
});
|
|
121
121
|
if (!_hasTimestamp(newOwnershipMetadata)) {
|
|
122
122
|
throw new Error(`Unable to retrieve timestamp from partitionKey "${partitionKey}", rowKey "${ownershipEntity.rowKey}"`);
|
|
@@ -156,7 +156,7 @@ export class TableCheckpointStore {
|
|
|
156
156
|
const partitionKey = `${fullyQualifiedNamespace} ${eventHubName} ${consumerGroup} Checkpoint`;
|
|
157
157
|
const checkpoints = [];
|
|
158
158
|
const entitiesIter = this._tableClient.listEntities({
|
|
159
|
-
queryOptions: { filter: odata `PartitionKey eq ${partitionKey}` }
|
|
159
|
+
queryOptions: { filter: odata `PartitionKey eq ${partitionKey}` },
|
|
160
160
|
});
|
|
161
161
|
try {
|
|
162
162
|
for (var entitiesIter_2 = __asyncValues(entitiesIter), entitiesIter_2_1; entitiesIter_2_1 = await entitiesIter_2.next(), !entitiesIter_2_1.done;) {
|
|
@@ -167,7 +167,7 @@ export class TableCheckpointStore {
|
|
|
167
167
|
fullyQualifiedNamespace,
|
|
168
168
|
partitionId: entity.rowKey,
|
|
169
169
|
offset: parseInt(entity.offset, 10),
|
|
170
|
-
sequenceNumber: parseInt(entity.sequencenumber, 10)
|
|
170
|
+
sequenceNumber: parseInt(entity.sequencenumber, 10),
|
|
171
171
|
});
|
|
172
172
|
}
|
|
173
173
|
}
|
|
@@ -195,7 +195,7 @@ export class TableCheckpointStore {
|
|
|
195
195
|
partitionKey: partitionKey,
|
|
196
196
|
rowKey: checkpoint.partitionId,
|
|
197
197
|
sequencenumber: checkpoint.sequenceNumber.toString(),
|
|
198
|
-
offset: checkpoint.offset.toString()
|
|
198
|
+
offset: checkpoint.offset.toString(),
|
|
199
199
|
};
|
|
200
200
|
try {
|
|
201
201
|
await this._tableClient.upsertEntity(checkpointEntity);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tableCheckpointStore.js","sourceRoot":"","sources":["../../src/tableCheckpointStore.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { Checkpoint, CheckpointStore, PartitionOwnership } from \"@azure/event-hubs\";\nimport { TableClient, TableInsertEntityHeaders, odata } from \"@azure/data-tables\";\nimport { logErrorStackTrace, logger } from \"./log\";\n\n/**\n *\n * Checks if the value contains a `Timestamp` field of type `string`.\n */\nfunction _hasTimestamp<T extends TableInsertEntityHeaders>(\n value: T\n): value is T & { Timestamp: string } {\n return typeof (value as any).Timestamp === \"string\";\n}\n\n/**\n * A checkpoint entity of type CheckpointEntity to be stored in the table\n * @internal\n *\n */\nexport interface CheckpointEntity {\n /**\n * The partitionKey is a composite key assembled in the following format:\n * `${fullyQualifiedNamespace} ${eventHubName} ${consumerGroup} Checkpoint`\n */\n partitionKey: string;\n /**\n * The rowKey is the partitionId\n *\n */\n rowKey: string;\n sequencenumber: string;\n offset: string;\n}\n\n/**\n * An ownership entity of type PartitionOwnership to be stored in the table\n * @internal\n * @hidden\n */\nexport interface PartitionOwnershipEntity {\n /**\n * The partitionKey is a composite key assembled in the following format:\n * `${fullyQualifiedNamespace} ${eventHubName} ${consumerGroup} Ownership`\n */\n partitionKey: string;\n /**\n * The rowKey is the partitionId\n *\n */\n rowKey: string;\n ownerid: string;\n}\n\n/**\n * An implementation of CheckpointStore that uses Azure Table Storage to persist checkpoint data.\n */\nexport class TableCheckpointStore implements CheckpointStore {\n private _tableClient: TableClient;\n\n constructor(tableClient: TableClient) {\n this._tableClient = tableClient;\n }\n\n /**\n * Get the list of all existing partition ownership from the underlying data store. May return empty\n * results if there are is no existing ownership information.\n * Partition Ownership contains the information on which `EventHubConsumerClient` subscribe call is currently processing the partition.\n *\n * @param fullyQualifiedNamespace - The fully qualified Event Hubs namespace. This is likely to be similar to\n * <yournamespace>.servicebus.windows.net.\n * @param eventHubName - The event hub name.\n * @param consumerGroup - The consumer group name.\n * @param options - A set of options that can be specified to influence the behavior of this method.\n * - `abortSignal`: A signal used to request operation cancellation.\n * - `tracingOptions`: Options for configuring tracing.\n * @returns Partition ownership details of all the partitions that have had an owner.\n */\n async listOwnership(\n fullyQualifiedNamespace: string,\n eventHubName: string,\n consumerGroup: string\n ): Promise<PartitionOwnership[]> {\n const partitionKey = `${fullyQualifiedNamespace} ${eventHubName} ${consumerGroup} Ownership`;\n const partitionOwnershipArray: PartitionOwnership[] = [];\n const entitiesIter = this._tableClient.listEntities<PartitionOwnershipEntity>({\n queryOptions: { filter: odata`PartitionKey eq ${partitionKey}` }\n });\n try {\n for await (const entity of entitiesIter) {\n if (!entity.timestamp) {\n throw new Error(\n `Unable to retrieve timestamp from partitionKey \"${partitionKey}\", rowKey \"${entity.rowKey}\"`\n );\n }\n\n const partitionOwnership: PartitionOwnership = {\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup,\n ownerId: entity.ownerid,\n partitionId: entity.rowKey,\n lastModifiedTimeInMs: new Date(entity.timestamp).getTime(),\n etag: entity.etag\n };\n partitionOwnershipArray.push(partitionOwnership);\n }\n return partitionOwnershipArray;\n } catch (err) {\n logger.warning(`Error occurred while fetching the list of entities`, err.message);\n logErrorStackTrace(err);\n if (err?.name === \"AbortError\") throw err;\n\n throw new Error(`Error occurred while fetching the list of entities. \\n${err}`);\n }\n }\n\n /**\n * Claim ownership of a list of partitions. This will return the list of partitions that were\n * successfully claimed.\n *\n * @param partitionOwnership - The list of partition ownership this instance is claiming to own.\n * @param options - A set of options that can be specified to influence the behavior of this method.\n * - `abortSignal`: A signal used to request operation cancellation.\n * - `tracingOptions`: Options for configuring tracing.\n * @returns A list partitions this instance successfully claimed ownership.\n */\n async claimOwnership(partitionOwnership: PartitionOwnership[]): Promise<PartitionOwnership[]> {\n const partitionOwnershipArray: PartitionOwnership[] = [];\n\n for (const ownership of partitionOwnership) {\n const updatedOwnership = { ...ownership };\n const partitionKey = `${ownership.fullyQualifiedNamespace} ${ownership.eventHubName} ${ownership.consumerGroup} Ownership`;\n const ownershipEntity: PartitionOwnershipEntity = {\n partitionKey: partitionKey,\n rowKey: ownership.partitionId,\n ownerid: ownership.ownerId\n };\n\n // When we have an etag, we know the entity existed.\n // If we encounter an error we should fail.\n try {\n if (ownership.etag) {\n const updatedMetadata = await this._tableClient.updateEntity(ownershipEntity, \"Replace\", {\n etag: ownership.etag\n });\n const entityRetrieved = await this._tableClient.getEntity(\n ownershipEntity.partitionKey,\n ownershipEntity.rowKey\n );\n if (!entityRetrieved.timestamp) {\n throw new Error(\n `Unable to retrieve timestamp from partitionKey \"${partitionKey}\", rowKey \"${entityRetrieved.rowKey}\"`\n );\n }\n updatedOwnership.lastModifiedTimeInMs = new Date(entityRetrieved.timestamp).getTime();\n updatedOwnership.etag = updatedMetadata.etag;\n partitionOwnershipArray.push(updatedOwnership);\n logger.info(\n `[${ownership.ownerId}] Claimed ownership successfully for partition: ${ownership.partitionId}`,\n `LastModifiedTime: ${ownership.lastModifiedTimeInMs}, ETag: ${ownership.etag}`\n );\n } else {\n const newOwnershipMetadata = await this._tableClient.createEntity(ownershipEntity, {\n requestOptions: {\n customHeaders: {\n Prefer: \"return-content\"\n }\n }\n });\n\n if (!_hasTimestamp(newOwnershipMetadata)) {\n throw new Error(\n `Unable to retrieve timestamp from partitionKey \"${partitionKey}\", rowKey \"${ownershipEntity.rowKey}\"`\n );\n }\n\n updatedOwnership.lastModifiedTimeInMs = new Date(\n newOwnershipMetadata.Timestamp\n ).getTime();\n updatedOwnership.etag = newOwnershipMetadata.etag;\n partitionOwnershipArray.push(updatedOwnership);\n }\n } catch (err) {\n if (err.statusCode === 412) {\n // etag failures (precondition not met) aren't fatal errors. They happen\n // as multiple consumers attempt to claim the same partition (first one wins)\n // and losers get this error.\n logger.verbose(\n `[${ownership.ownerId}] Did not claim partition ${ownership.partitionId}. Another processor has already claimed it.`\n );\n continue;\n }\n logger.warning(\n `Error occurred while claiming ownership for partition: ${ownership.partitionId}`,\n err.message\n );\n logErrorStackTrace(err);\n }\n }\n return partitionOwnershipArray;\n }\n\n /**\n * Lists all the checkpoints in a data store for a given namespace, eventhub and consumer group.\n *\n * @param fullyQualifiedNamespace - The fully qualified Event Hubs namespace. This is likely to be similar to\n * <yournamespace>.servicebus.windows.net.\n * @param eventHubName - The event hub name.\n * @param consumerGroup - The consumer group name.\n * @param options - A set of options that can be specified to influence the behavior of this method.\n * - `abortSignal`: A signal used to request operation cancellation.\n * - `tracingOptions`: Options for configuring tracing.\n */\n async listCheckpoints(\n fullyQualifiedNamespace: string,\n eventHubName: string,\n consumerGroup: string\n ): Promise<Checkpoint[]> {\n const partitionKey = `${fullyQualifiedNamespace} ${eventHubName} ${consumerGroup} Checkpoint`;\n const checkpoints: Checkpoint[] = [];\n const entitiesIter = this._tableClient.listEntities<CheckpointEntity>({\n queryOptions: { filter: odata`PartitionKey eq ${partitionKey}` }\n });\n for await (const entity of entitiesIter) {\n checkpoints.push({\n consumerGroup,\n eventHubName,\n fullyQualifiedNamespace,\n partitionId: entity.rowKey,\n offset: parseInt(entity.offset, 10),\n sequenceNumber: parseInt(entity.sequencenumber, 10)\n });\n }\n return checkpoints;\n }\n\n /**\n * Updates the checkpoint in the data store for a partition.\n *\n * @param checkpoint - The checkpoint.\n * @param options - A set of options that can be specified to influence the behavior of this method.\n * - `abortSignal`: A signal used to request operation cancellation.\n * - `tracingOptions`: Options for configuring tracing.\n * @returns A promise that resolves when the checkpoint has been updated.\n */\n async updateCheckpoint(checkpoint: Checkpoint): Promise<void> {\n const partitionKey = `${checkpoint.fullyQualifiedNamespace} ${checkpoint.eventHubName} ${checkpoint.consumerGroup} Checkpoint`;\n const checkpointEntity: CheckpointEntity = {\n partitionKey: partitionKey,\n rowKey: checkpoint.partitionId,\n sequencenumber: checkpoint.sequenceNumber.toString(),\n offset: checkpoint.offset.toString()\n };\n try {\n await this._tableClient.upsertEntity(checkpointEntity);\n logger.verbose(`Updated checkpoint successfully for partition: ${checkpoint.partitionId}`);\n return;\n } catch (err) {\n logger.verbose(\n `Error occurred while upating the checkpoint for partition: ${checkpoint.partitionId}.`,\n err.message\n );\n throw err;\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tableCheckpointStore.js","sourceRoot":"","sources":["../../src/tableCheckpointStore.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { Checkpoint, CheckpointStore, PartitionOwnership } from \"@azure/event-hubs\";\nimport { TableClient, TableInsertEntityHeaders, odata } from \"@azure/data-tables\";\nimport { logErrorStackTrace, logger } from \"./log\";\n\n/**\n *\n * Checks if the value contains a `Timestamp` field of type `string`.\n */\nfunction _hasTimestamp<T extends TableInsertEntityHeaders>(\n value: T\n): value is T & { Timestamp: string } {\n return typeof (value as any).Timestamp === \"string\";\n}\n\n/**\n * A checkpoint entity of type CheckpointEntity to be stored in the table\n * @internal\n *\n */\nexport interface CheckpointEntity {\n /**\n * The partitionKey is a composite key assembled in the following format:\n * `${fullyQualifiedNamespace} ${eventHubName} ${consumerGroup} Checkpoint`\n */\n partitionKey: string;\n /**\n * The rowKey is the partitionId\n *\n */\n rowKey: string;\n sequencenumber: string;\n offset: string;\n}\n\n/**\n * An ownership entity of type PartitionOwnership to be stored in the table\n * @internal\n */\nexport interface PartitionOwnershipEntity {\n /**\n * The partitionKey is a composite key assembled in the following format:\n * `${fullyQualifiedNamespace} ${eventHubName} ${consumerGroup} Ownership`\n */\n partitionKey: string;\n /**\n * The rowKey is the partitionId\n *\n */\n rowKey: string;\n ownerid: string;\n}\n\n/**\n * An implementation of CheckpointStore that uses Azure Table Storage to persist checkpoint data.\n */\nexport class TableCheckpointStore implements CheckpointStore {\n private _tableClient: TableClient;\n\n constructor(tableClient: TableClient) {\n this._tableClient = tableClient;\n }\n\n /**\n * Get the list of all existing partition ownership from the underlying data store. May return empty\n * results if there are is no existing ownership information.\n * Partition Ownership contains the information on which `EventHubConsumerClient` subscribe call is currently processing the partition.\n *\n * @param fullyQualifiedNamespace - The fully qualified Event Hubs namespace. This is likely to be similar to\n * <yournamespace>.servicebus.windows.net.\n * @param eventHubName - The event hub name.\n * @param consumerGroup - The consumer group name.\n * @param options - A set of options that can be specified to influence the behavior of this method.\n * - `abortSignal`: A signal used to request operation cancellation.\n * - `tracingOptions`: Options for configuring tracing.\n * @returns Partition ownership details of all the partitions that have had an owner.\n */\n async listOwnership(\n fullyQualifiedNamespace: string,\n eventHubName: string,\n consumerGroup: string\n ): Promise<PartitionOwnership[]> {\n const partitionKey = `${fullyQualifiedNamespace} ${eventHubName} ${consumerGroup} Ownership`;\n const partitionOwnershipArray: PartitionOwnership[] = [];\n const entitiesIter = this._tableClient.listEntities<PartitionOwnershipEntity>({\n queryOptions: { filter: odata`PartitionKey eq ${partitionKey}` },\n });\n try {\n for await (const entity of entitiesIter) {\n if (!entity.timestamp) {\n throw new Error(\n `Unable to retrieve timestamp from partitionKey \"${partitionKey}\", rowKey \"${entity.rowKey}\"`\n );\n }\n\n const partitionOwnership: PartitionOwnership = {\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup,\n ownerId: entity.ownerid,\n partitionId: entity.rowKey,\n lastModifiedTimeInMs: new Date(entity.timestamp).getTime(),\n etag: entity.etag,\n };\n partitionOwnershipArray.push(partitionOwnership);\n }\n return partitionOwnershipArray;\n } catch (err) {\n logger.warning(`Error occurred while fetching the list of entities`, err.message);\n logErrorStackTrace(err);\n if (err?.name === \"AbortError\") throw err;\n\n throw new Error(`Error occurred while fetching the list of entities. \\n${err}`);\n }\n }\n\n /**\n * Claim ownership of a list of partitions. This will return the list of partitions that were\n * successfully claimed.\n *\n * @param partitionOwnership - The list of partition ownership this instance is claiming to own.\n * @param options - A set of options that can be specified to influence the behavior of this method.\n * - `abortSignal`: A signal used to request operation cancellation.\n * - `tracingOptions`: Options for configuring tracing.\n * @returns A list partitions this instance successfully claimed ownership.\n */\n async claimOwnership(partitionOwnership: PartitionOwnership[]): Promise<PartitionOwnership[]> {\n const partitionOwnershipArray: PartitionOwnership[] = [];\n\n for (const ownership of partitionOwnership) {\n const updatedOwnership = { ...ownership };\n const partitionKey = `${ownership.fullyQualifiedNamespace} ${ownership.eventHubName} ${ownership.consumerGroup} Ownership`;\n const ownershipEntity: PartitionOwnershipEntity = {\n partitionKey: partitionKey,\n rowKey: ownership.partitionId,\n ownerid: ownership.ownerId,\n };\n\n // When we have an etag, we know the entity existed.\n // If we encounter an error we should fail.\n try {\n if (ownership.etag) {\n const updatedMetadata = await this._tableClient.updateEntity(ownershipEntity, \"Replace\", {\n etag: ownership.etag,\n });\n const entityRetrieved = await this._tableClient.getEntity(\n ownershipEntity.partitionKey,\n ownershipEntity.rowKey\n );\n if (!entityRetrieved.timestamp) {\n throw new Error(\n `Unable to retrieve timestamp from partitionKey \"${partitionKey}\", rowKey \"${entityRetrieved.rowKey}\"`\n );\n }\n updatedOwnership.lastModifiedTimeInMs = new Date(entityRetrieved.timestamp).getTime();\n updatedOwnership.etag = updatedMetadata.etag;\n partitionOwnershipArray.push(updatedOwnership);\n logger.info(\n `[${ownership.ownerId}] Claimed ownership successfully for partition: ${ownership.partitionId}`,\n `LastModifiedTime: ${ownership.lastModifiedTimeInMs}, ETag: ${ownership.etag}`\n );\n } else {\n const newOwnershipMetadata = await this._tableClient.createEntity(ownershipEntity, {\n requestOptions: {\n customHeaders: {\n Prefer: \"return-content\",\n },\n },\n });\n\n if (!_hasTimestamp(newOwnershipMetadata)) {\n throw new Error(\n `Unable to retrieve timestamp from partitionKey \"${partitionKey}\", rowKey \"${ownershipEntity.rowKey}\"`\n );\n }\n\n updatedOwnership.lastModifiedTimeInMs = new Date(\n newOwnershipMetadata.Timestamp\n ).getTime();\n updatedOwnership.etag = newOwnershipMetadata.etag;\n partitionOwnershipArray.push(updatedOwnership);\n }\n } catch (err) {\n if (err.statusCode === 412) {\n // etag failures (precondition not met) aren't fatal errors. They happen\n // as multiple consumers attempt to claim the same partition (first one wins)\n // and losers get this error.\n logger.verbose(\n `[${ownership.ownerId}] Did not claim partition ${ownership.partitionId}. Another processor has already claimed it.`\n );\n continue;\n }\n logger.warning(\n `Error occurred while claiming ownership for partition: ${ownership.partitionId}`,\n err.message\n );\n logErrorStackTrace(err);\n }\n }\n return partitionOwnershipArray;\n }\n\n /**\n * Lists all the checkpoints in a data store for a given namespace, eventhub and consumer group.\n *\n * @param fullyQualifiedNamespace - The fully qualified Event Hubs namespace. This is likely to be similar to\n * <yournamespace>.servicebus.windows.net.\n * @param eventHubName - The event hub name.\n * @param consumerGroup - The consumer group name.\n * @param options - A set of options that can be specified to influence the behavior of this method.\n * - `abortSignal`: A signal used to request operation cancellation.\n * - `tracingOptions`: Options for configuring tracing.\n */\n async listCheckpoints(\n fullyQualifiedNamespace: string,\n eventHubName: string,\n consumerGroup: string\n ): Promise<Checkpoint[]> {\n const partitionKey = `${fullyQualifiedNamespace} ${eventHubName} ${consumerGroup} Checkpoint`;\n const checkpoints: Checkpoint[] = [];\n const entitiesIter = this._tableClient.listEntities<CheckpointEntity>({\n queryOptions: { filter: odata`PartitionKey eq ${partitionKey}` },\n });\n for await (const entity of entitiesIter) {\n checkpoints.push({\n consumerGroup,\n eventHubName,\n fullyQualifiedNamespace,\n partitionId: entity.rowKey,\n offset: parseInt(entity.offset, 10),\n sequenceNumber: parseInt(entity.sequencenumber, 10),\n });\n }\n return checkpoints;\n }\n\n /**\n * Updates the checkpoint in the data store for a partition.\n *\n * @param checkpoint - The checkpoint.\n * @param options - A set of options that can be specified to influence the behavior of this method.\n * - `abortSignal`: A signal used to request operation cancellation.\n * - `tracingOptions`: Options for configuring tracing.\n * @returns A promise that resolves when the checkpoint has been updated.\n */\n async updateCheckpoint(checkpoint: Checkpoint): Promise<void> {\n const partitionKey = `${checkpoint.fullyQualifiedNamespace} ${checkpoint.eventHubName} ${checkpoint.consumerGroup} Checkpoint`;\n const checkpointEntity: CheckpointEntity = {\n partitionKey: partitionKey,\n rowKey: checkpoint.partitionId,\n sequencenumber: checkpoint.sequenceNumber.toString(),\n offset: checkpoint.offset.toString(),\n };\n try {\n await this._tableClient.upsertEntity(checkpointEntity);\n logger.verbose(`Updated checkpoint successfully for partition: ${checkpoint.partitionId}`);\n return;\n } catch (err) {\n logger.verbose(\n `Error occurred while upating the checkpoint for partition: ${checkpoint.partitionId}.`,\n err.message\n );\n throw err;\n }\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@azure/eventhubs-checkpointstore-table",
|
|
3
3
|
"sdk-type": "client",
|
|
4
|
-
"version": "1.0.0-alpha.
|
|
4
|
+
"version": "1.0.0-alpha.20220128.3",
|
|
5
5
|
"description": "An Azure Storage Table solution to store checkpoints when using Event Hubs.",
|
|
6
6
|
"author": "Microsoft Corporation",
|
|
7
7
|
"license": "MIT",
|
|
@@ -32,13 +32,13 @@
|
|
|
32
32
|
],
|
|
33
33
|
"scripts": {
|
|
34
34
|
"audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit",
|
|
35
|
-
"build:browser": "tsc -p . &&
|
|
36
|
-
"build:node": "tsc -p . &&
|
|
35
|
+
"build:browser": "tsc -p . && dev-tool run bundle",
|
|
36
|
+
"build:node": "tsc -p . && dev-tool run bundle",
|
|
37
37
|
"build:samples": "echo not implemented",
|
|
38
|
-
"build:test:browser": "tsc -p . &&
|
|
39
|
-
"build:test:node": "tsc -p . &&
|
|
38
|
+
"build:test:browser": "tsc -p . && dev-tool run bundle",
|
|
39
|
+
"build:test:node": "tsc -p . && dev-tool run bundle",
|
|
40
40
|
"build:test": "npm run build:test:node",
|
|
41
|
-
"build": "npm run clean && tsc -p . &&
|
|
41
|
+
"build": "npm run clean && tsc -p . && dev-tool run bundle && api-extractor run --local",
|
|
42
42
|
"check-format": "prettier --list-different --config ../../../.prettierrc.json --ignore-path ../../../.prettierignore \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"",
|
|
43
43
|
"clean": "rimraf dist dist-* typings *.tgz *.log",
|
|
44
44
|
"execute:samples": "echo skipped",
|
|
@@ -55,8 +55,7 @@
|
|
|
55
55
|
"test": "npm run build:test && npm run unit-test && npm run integration-test",
|
|
56
56
|
"unit-test:browser": "echo skipped",
|
|
57
57
|
"unit-test:node": "echo skipped",
|
|
58
|
-
"unit-test": "npm run unit-test:node && npm run unit-test:browser"
|
|
59
|
-
"docs": "typedoc --excludePrivate --excludeNotExported --excludeExternals --stripInternal --mode file --out ./dist/docs ./src"
|
|
58
|
+
"unit-test": "npm run unit-test:node && npm run unit-test:browser"
|
|
60
59
|
},
|
|
61
60
|
"dependencies": {
|
|
62
61
|
"@azure/abort-controller": "^1.0.0",
|
|
@@ -97,15 +96,13 @@
|
|
|
97
96
|
"karma-mocha-reporter": "^2.2.5",
|
|
98
97
|
"karma-sourcemap-loader": "^0.3.8",
|
|
99
98
|
"mocha": "^7.1.1",
|
|
100
|
-
"mocha-junit-reporter": "^
|
|
99
|
+
"mocha-junit-reporter": "^2.0.0",
|
|
101
100
|
"nyc": "^15.0.0",
|
|
102
|
-
"prettier": "^
|
|
101
|
+
"prettier": "^2.5.1",
|
|
103
102
|
"rimraf": "^3.0.0",
|
|
104
|
-
"rollup": "^1.16.3",
|
|
105
103
|
"ts-node": "^10.0.0",
|
|
106
104
|
"typescript": "~4.2.0",
|
|
107
|
-
"util": "^0.12.1"
|
|
108
|
-
"typedoc": "0.15.2"
|
|
105
|
+
"util": "^0.12.1"
|
|
109
106
|
},
|
|
110
107
|
"//sampleConfiguration": {
|
|
111
108
|
"skipFolder": true
|