@azure/eventhubs-checkpointstore-blob 2.0.1-alpha.20250619.1 → 2.0.1-alpha.20250730.1

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.
@@ -1,12 +1,12 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
- import { __asyncValues } from "tslib";
4
3
  import { logger, logErrorStackTrace } from "./log.js";
5
4
  import { throwTypeErrorIfParameterMissing } from "./util/error.js";
6
5
  /**
7
6
  * An implementation of CheckpointStore that uses Azure Blob Storage to persist checkpoint data.
8
7
  */
9
8
  export class BlobCheckpointStore {
9
+ _containerClient;
10
10
  /**
11
11
  * Constructs a new instance of {@link BlobCheckpointStore}
12
12
  * @param containerClient - An instance of a storage blob ContainerClient.
@@ -29,8 +29,6 @@ export class BlobCheckpointStore {
29
29
  * @returns Partition ownership details of all the partitions that have had an owner.
30
30
  */
31
31
  async listOwnership(fullyQualifiedNamespace, eventHubName, consumerGroup, options = {}) {
32
- var _a, e_1, _b, _c;
33
- var _d;
34
32
  const partitionOwnershipArray = [];
35
33
  const { abortSignal, tracingOptions } = options;
36
34
  const blobPrefix = BlobCheckpointStore.getBlobPrefix({
@@ -46,42 +44,30 @@ export class BlobCheckpointStore {
46
44
  prefix: blobPrefix,
47
45
  tracingOptions,
48
46
  });
49
- try {
50
- for (var _e = true, blobs_1 = __asyncValues(blobs), blobs_1_1; blobs_1_1 = await blobs_1.next(), _a = blobs_1_1.done, !_a; _e = true) {
51
- _c = blobs_1_1.value;
52
- _e = false;
53
- const blob = _c;
54
- const blobPath = blob.name.split("/");
55
- const blobName = blobPath[blobPath.length - 1];
56
- const ownershipMetadata = (_d = blob.metadata) !== null && _d !== void 0 ? _d : {};
57
- if (ownershipMetadata.ownerid == null) {
58
- throw new Error(`Missing ownerid in metadata for blob ${blob.name}`);
59
- }
60
- const partitionOwnership = {
61
- fullyQualifiedNamespace,
62
- eventHubName,
63
- consumerGroup: consumerGroup,
64
- ownerId: ownershipMetadata.ownerid,
65
- partitionId: blobName,
66
- lastModifiedTimeInMs: blob.properties.lastModified && blob.properties.lastModified.getTime(),
67
- etag: blob.properties.etag,
68
- };
69
- partitionOwnershipArray.push(partitionOwnership);
70
- }
71
- }
72
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
73
- finally {
74
- try {
75
- if (!_e && !_a && (_b = blobs_1.return)) await _b.call(blobs_1);
47
+ for await (const blob of blobs) {
48
+ const blobPath = blob.name.split("/");
49
+ const blobName = blobPath[blobPath.length - 1];
50
+ const ownershipMetadata = blob.metadata ?? {};
51
+ if (ownershipMetadata.ownerid == null) {
52
+ throw new Error(`Missing ownerid in metadata for blob ${blob.name}`);
76
53
  }
77
- finally { if (e_1) throw e_1.error; }
54
+ const partitionOwnership = {
55
+ fullyQualifiedNamespace,
56
+ eventHubName,
57
+ consumerGroup: consumerGroup,
58
+ ownerId: ownershipMetadata.ownerid,
59
+ partitionId: blobName,
60
+ lastModifiedTimeInMs: blob.properties.lastModified && blob.properties.lastModified.getTime(),
61
+ etag: blob.properties.etag,
62
+ };
63
+ partitionOwnershipArray.push(partitionOwnership);
78
64
  }
79
65
  return partitionOwnershipArray;
80
66
  }
81
67
  catch (err) {
82
68
  logger.warning(`Error occurred while fetching the list of blobs`, err.message);
83
69
  logErrorStackTrace(err);
84
- if ((err === null || err === void 0 ? void 0 : err.name) === "AbortError")
70
+ if (err?.name === "AbortError")
85
71
  throw err;
86
72
  throw new Error(`Error occurred while fetching the list of blobs. \n${err}`);
87
73
  }
@@ -99,7 +85,7 @@ export class BlobCheckpointStore {
99
85
  async claimOwnership(partitionOwnership, options = {}) {
100
86
  const partitionOwnershipArray = [];
101
87
  for (const ownership of partitionOwnership) {
102
- const blobName = BlobCheckpointStore.getBlobPrefix(Object.assign({ type: "ownership" }, ownership));
88
+ const blobName = BlobCheckpointStore.getBlobPrefix({ type: "ownership", ...ownership });
103
89
  try {
104
90
  const updatedBlobResponse = await this._setBlobMetadata(blobName, {
105
91
  ownerid: ownership.ownerId,
@@ -139,8 +125,6 @@ export class BlobCheckpointStore {
139
125
  * - `tracingOptions`: Options for configuring tracing.
140
126
  */
141
127
  async listCheckpoints(fullyQualifiedNamespace, eventHubName, consumerGroup, options = {}) {
142
- var _a, e_2, _b, _c;
143
- var _d;
144
128
  const { abortSignal, tracingOptions } = options;
145
129
  const blobPrefix = BlobCheckpointStore.getBlobPrefix({
146
130
  type: "checkpoint",
@@ -155,35 +139,23 @@ export class BlobCheckpointStore {
155
139
  tracingOptions,
156
140
  });
157
141
  const checkpoints = [];
158
- try {
159
- for (var _e = true, blobs_2 = __asyncValues(blobs), blobs_2_1; blobs_2_1 = await blobs_2.next(), _a = blobs_2_1.done, !_a; _e = true) {
160
- _c = blobs_2_1.value;
161
- _e = false;
162
- const blob = _c;
163
- const blobPath = blob.name.split("/");
164
- const blobName = blobPath[blobPath.length - 1];
165
- const checkpointMetadata = (_d = blob.metadata) !== null && _d !== void 0 ? _d : {};
166
- const offset = checkpointMetadata.offset;
167
- if (offset == null) {
168
- throw new Error(`Missing metadata property 'offset' on blob '${blob.name}'`);
169
- }
170
- const sequenceNumber = parseIntOrThrow(blob.name, "sequencenumber", checkpointMetadata.sequencenumber);
171
- checkpoints.push({
172
- consumerGroup,
173
- eventHubName,
174
- fullyQualifiedNamespace,
175
- partitionId: blobName,
176
- offset,
177
- sequenceNumber,
178
- });
142
+ for await (const blob of blobs) {
143
+ const blobPath = blob.name.split("/");
144
+ const blobName = blobPath[blobPath.length - 1];
145
+ const checkpointMetadata = blob.metadata ?? {};
146
+ const offset = checkpointMetadata.offset;
147
+ if (offset == null) {
148
+ throw new Error(`Missing metadata property 'offset' on blob '${blob.name}'`);
179
149
  }
180
- }
181
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
182
- finally {
183
- try {
184
- if (!_e && !_a && (_b = blobs_2.return)) await _b.call(blobs_2);
185
- }
186
- finally { if (e_2) throw e_2.error; }
150
+ const sequenceNumber = parseIntOrThrow(blob.name, "sequencenumber", checkpointMetadata.sequencenumber);
151
+ checkpoints.push({
152
+ consumerGroup,
153
+ eventHubName,
154
+ fullyQualifiedNamespace,
155
+ partitionId: blobName,
156
+ offset,
157
+ sequenceNumber,
158
+ });
187
159
  }
188
160
  return checkpoints;
189
161
  }
@@ -199,7 +171,7 @@ export class BlobCheckpointStore {
199
171
  async updateCheckpoint(checkpoint, options = {}) {
200
172
  throwTypeErrorIfParameterMissing("updateCheckpoint", "sequenceNumber", checkpoint.sequenceNumber);
201
173
  throwTypeErrorIfParameterMissing("updateCheckpoint", "offset", checkpoint.offset);
202
- const blobName = BlobCheckpointStore.getBlobPrefix(Object.assign({ type: "checkpoint" }, checkpoint));
174
+ const blobName = BlobCheckpointStore.getBlobPrefix({ type: "checkpoint", ...checkpoint });
203
175
  try {
204
176
  const metadataResponse = await this._setBlobMetadata(blobName, {
205
177
  sequencenumber: checkpoint.sequenceNumber.toString(),
@@ -211,7 +183,7 @@ export class BlobCheckpointStore {
211
183
  catch (err) {
212
184
  logger.warning(`Error occurred while updating the checkpoint for partition: ${checkpoint.partitionId}.`, err.message);
213
185
  logErrorStackTrace(err);
214
- if ((err === null || err === void 0 ? void 0 : err.name) === "AbortError")
186
+ if (err?.name === "AbortError")
215
187
  throw err;
216
188
  throw err;
217
189
  }
@@ -255,11 +227,11 @@ export class BlobCheckpointStore {
255
227
  }
256
228
  catch (err) {
257
229
  // Check if the error is `BlobNotFound` and fallback to `upload` if it is.
258
- if ((err === null || err === void 0 ? void 0 : err.name) !== "RestError") {
230
+ if (err?.name !== "RestError") {
259
231
  throw err;
260
232
  }
261
233
  const errorDetails = err.details;
262
- const errorCode = errorDetails === null || errorDetails === void 0 ? void 0 : errorDetails.errorCode;
234
+ const errorCode = errorDetails?.errorCode;
263
235
  if (!errorCode || errorCode !== "BlobNotFound") {
264
236
  throw err;
265
237
  }
@@ -1 +1 @@
1
- {"version":3,"file":"blobCheckpointStore.js","sourceRoot":"","sources":["../../src/blobCheckpointStore.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n CheckpointStore,\n PartitionOwnership,\n Checkpoint,\n OperationOptions,\n} from \"@azure/event-hubs\";\nimport type { Metadata, RestError, BlobSetMetadataResponse } from \"@azure/storage-blob\";\nimport { logger, logErrorStackTrace } from \"./log.js\";\nimport type { ContainerClientLike } from \"./storageBlobInterfaces.js\";\nimport { throwTypeErrorIfParameterMissing } from \"./util/error.js\";\n\n/**\n * An implementation of CheckpointStore that uses Azure Blob Storage to persist checkpoint data.\n */\nexport class BlobCheckpointStore implements CheckpointStore {\n private _containerClient: ContainerClientLike;\n\n /**\n * Constructs a new instance of {@link BlobCheckpointStore}\n * @param containerClient - An instance of a storage blob ContainerClient.\n */\n constructor(containerClient: ContainerClientLike) {\n this._containerClient = containerClient;\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 options: OperationOptions = {},\n ): Promise<PartitionOwnership[]> {\n const partitionOwnershipArray: PartitionOwnership[] = [];\n const { abortSignal, tracingOptions } = options;\n\n const blobPrefix = BlobCheckpointStore.getBlobPrefix({\n type: \"ownership\",\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup: consumerGroup,\n });\n\n try {\n const blobs = this._containerClient.listBlobsFlat({\n abortSignal,\n includeMetadata: true,\n prefix: blobPrefix,\n tracingOptions,\n });\n\n for await (const blob of blobs) {\n const blobPath = blob.name.split(\"/\");\n const blobName = blobPath[blobPath.length - 1];\n\n const ownershipMetadata = (blob.metadata as OwnershipMetadata) ?? {};\n\n if (ownershipMetadata.ownerid == null) {\n throw new Error(`Missing ownerid in metadata for blob ${blob.name}`);\n }\n\n const partitionOwnership: PartitionOwnership = {\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup: consumerGroup,\n ownerId: ownershipMetadata.ownerid,\n partitionId: blobName,\n lastModifiedTimeInMs:\n blob.properties.lastModified && blob.properties.lastModified.getTime(),\n etag: blob.properties.etag,\n };\n partitionOwnershipArray.push(partitionOwnership);\n }\n return partitionOwnershipArray;\n } catch (err: any) {\n logger.warning(`Error occurred while fetching the list of blobs`, err.message);\n logErrorStackTrace(err);\n\n if (err?.name === \"AbortError\") throw err;\n\n throw new Error(`Error occurred while fetching the list of blobs. \\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(\n partitionOwnership: PartitionOwnership[],\n options: OperationOptions = {},\n ): Promise<PartitionOwnership[]> {\n const partitionOwnershipArray: PartitionOwnership[] = [];\n for (const ownership of partitionOwnership) {\n const blobName = BlobCheckpointStore.getBlobPrefix({ type: \"ownership\", ...ownership });\n try {\n const updatedBlobResponse = await this._setBlobMetadata(\n blobName,\n {\n ownerid: ownership.ownerId,\n },\n ownership.etag,\n options,\n );\n\n if (updatedBlobResponse.lastModified) {\n ownership.lastModifiedTimeInMs = updatedBlobResponse.lastModified.getTime();\n }\n\n ownership.etag = updatedBlobResponse.etag;\n partitionOwnershipArray.push(ownership);\n logger.info(\n `[${ownership.ownerId}] Claimed ownership successfully for partition: ${ownership.partitionId}`,\n `LastModifiedTime: ${ownership.lastModifiedTimeInMs}, ETag: ${ownership.etag}`,\n );\n } catch (err: any) {\n const restError = err as RestError;\n\n if (restError.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\n logger.warning(\n `Error occurred while claiming ownership for partition: ${ownership.partitionId}`,\n err.message,\n );\n logErrorStackTrace(err);\n\n throw 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 options: OperationOptions = {},\n ): Promise<Checkpoint[]> {\n const { abortSignal, tracingOptions } = options;\n const blobPrefix = BlobCheckpointStore.getBlobPrefix({\n type: \"checkpoint\",\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup,\n });\n\n const blobs = this._containerClient.listBlobsFlat({\n abortSignal,\n includeMetadata: true,\n prefix: blobPrefix,\n tracingOptions,\n });\n\n const checkpoints: Checkpoint[] = [];\n\n for await (const blob of blobs) {\n const blobPath = blob.name.split(\"/\");\n const blobName = blobPath[blobPath.length - 1];\n\n const checkpointMetadata = (blob.metadata as CheckpointMetadata) ?? {};\n\n const offset = checkpointMetadata.offset;\n if (offset == null) {\n throw new Error(`Missing metadata property 'offset' on blob '${blob.name}'`);\n }\n const sequenceNumber = parseIntOrThrow(\n blob.name,\n \"sequencenumber\",\n checkpointMetadata.sequencenumber,\n );\n\n checkpoints.push({\n consumerGroup,\n eventHubName,\n fullyQualifiedNamespace,\n partitionId: blobName,\n offset,\n sequenceNumber,\n });\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 The new etag on successful update.\n */\n async updateCheckpoint(checkpoint: Checkpoint, options: OperationOptions = {}): Promise<void> {\n throwTypeErrorIfParameterMissing(\n \"updateCheckpoint\",\n \"sequenceNumber\",\n checkpoint.sequenceNumber,\n );\n throwTypeErrorIfParameterMissing(\"updateCheckpoint\", \"offset\", checkpoint.offset);\n\n const blobName = BlobCheckpointStore.getBlobPrefix({ type: \"checkpoint\", ...checkpoint });\n try {\n const metadataResponse = await this._setBlobMetadata(\n blobName,\n {\n sequencenumber: checkpoint.sequenceNumber.toString(),\n offset: checkpoint.offset.toString(),\n },\n undefined,\n options,\n );\n\n logger.verbose(\n `Updated checkpoint successfully for partition: ${checkpoint.partitionId}`,\n `LastModifiedTime: ${metadataResponse.lastModified!.toISOString()}, ETag: ${\n metadataResponse.etag\n }`,\n );\n return;\n } catch (err: any) {\n logger.warning(\n `Error occurred while updating the checkpoint for partition: ${checkpoint.partitionId}.`,\n err.message,\n );\n logErrorStackTrace(err);\n\n if (err?.name === \"AbortError\") throw err;\n\n throw err;\n }\n }\n\n private static getBlobPrefix(params: {\n type: \"ownership\" | \"checkpoint\";\n fullyQualifiedNamespace: string;\n eventHubName: string;\n consumerGroup: string;\n partitionId?: string;\n }): string {\n // none of these are case-sensitive in eventhubs so we need to make sure we don't accidentally allow\n // the user to create a case-sensitive blob for their state!\n const consumerGroupName = params.consumerGroup.toLowerCase();\n const eventHubName = params.eventHubName.toLowerCase();\n const fullyQualifiedNamespace = params.fullyQualifiedNamespace.toLowerCase();\n\n if (params.partitionId) {\n return `${fullyQualifiedNamespace}/${eventHubName}/${consumerGroupName}/${params.type}/${params.partitionId}`;\n } else {\n return `${fullyQualifiedNamespace}/${eventHubName}/${consumerGroupName}/${params.type}/`;\n }\n }\n\n private async _setBlobMetadata(\n blobName: string,\n metadata: OwnershipMetadata | CheckpointMetadata,\n etag: string | undefined,\n options: OperationOptions = {},\n ): Promise<BlobSetMetadataResponse> {\n const { abortSignal, tracingOptions } = options;\n const blockBlobClient = this._containerClient.getBlobClient(blobName).getBlockBlobClient();\n\n // When we have an etag, we know the blob existed.\n // If we encounter an error we should fail.\n if (etag) {\n return blockBlobClient.setMetadata(metadata as Metadata, {\n abortSignal,\n conditions: {\n ifMatch: etag,\n },\n tracingOptions,\n });\n } else {\n try {\n // Attempt to set metadata, and fallback to upload if the blob doesn't already exist.\n // This avoids poor performance in storage accounts with soft-delete or blob versioning enabled.\n // https://github.com/Azure/azure-sdk-for-js/issues/10132\n return await blockBlobClient.setMetadata(metadata as Metadata, {\n abortSignal,\n tracingOptions,\n });\n } catch (err: any) {\n // Check if the error is `BlobNotFound` and fallback to `upload` if it is.\n if (err?.name !== \"RestError\") {\n throw err;\n }\n const errorDetails = (err as RestError).details as { [field: string]: string } | undefined;\n const errorCode = errorDetails?.errorCode;\n if (!errorCode || errorCode !== \"BlobNotFound\") {\n throw err;\n }\n\n return blockBlobClient.upload(\"\", 0, {\n abortSignal,\n metadata: metadata as Metadata,\n tracingOptions,\n });\n }\n }\n }\n}\n\ntype OwnershipMetadata = {\n [k in \"ownerid\"]: string | undefined;\n};\n\ntype CheckpointMetadata = {\n [k in \"sequencenumber\" | \"offset\"]: string | undefined;\n};\n\n/**\n * @internal\n */\nexport function parseIntOrThrow(\n blobName: string,\n fieldName: string,\n numStr: string | undefined,\n): number {\n if (numStr == null) {\n throw new Error(`Missing metadata property '${fieldName}' on blob '${blobName}'`);\n }\n\n const num = parseInt(numStr, 10);\n\n if (isNaN(num)) {\n throw new Error(\n `Failed to parse metadata property '${fieldName}' on blob '${blobName}' as a number`,\n );\n }\n\n return num;\n}\n"]}
1
+ {"version":3,"file":"blobCheckpointStore.js","sourceRoot":"","sources":["../../src/blobCheckpointStore.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n CheckpointStore,\n PartitionOwnership,\n Checkpoint,\n OperationOptions,\n} from \"@azure/event-hubs\";\nimport type { Metadata, RestError, BlobSetMetadataResponse } from \"@azure/storage-blob\";\nimport { logger, logErrorStackTrace } from \"./log.js\";\nimport type { ContainerClientLike } from \"./storageBlobInterfaces.js\";\nimport { throwTypeErrorIfParameterMissing } from \"./util/error.js\";\n\n/**\n * An implementation of CheckpointStore that uses Azure Blob Storage to persist checkpoint data.\n */\nexport class BlobCheckpointStore implements CheckpointStore {\n private _containerClient: ContainerClientLike;\n\n /**\n * Constructs a new instance of {@link BlobCheckpointStore}\n * @param containerClient - An instance of a storage blob ContainerClient.\n */\n constructor(containerClient: ContainerClientLike) {\n this._containerClient = containerClient;\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 options: OperationOptions = {},\n ): Promise<PartitionOwnership[]> {\n const partitionOwnershipArray: PartitionOwnership[] = [];\n const { abortSignal, tracingOptions } = options;\n\n const blobPrefix = BlobCheckpointStore.getBlobPrefix({\n type: \"ownership\",\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup: consumerGroup,\n });\n\n try {\n const blobs = this._containerClient.listBlobsFlat({\n abortSignal,\n includeMetadata: true,\n prefix: blobPrefix,\n tracingOptions,\n });\n\n for await (const blob of blobs) {\n const blobPath = blob.name.split(\"/\");\n const blobName = blobPath[blobPath.length - 1];\n\n const ownershipMetadata = (blob.metadata as OwnershipMetadata) ?? {};\n\n if (ownershipMetadata.ownerid == null) {\n throw new Error(`Missing ownerid in metadata for blob ${blob.name}`);\n }\n\n const partitionOwnership: PartitionOwnership = {\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup: consumerGroup,\n ownerId: ownershipMetadata.ownerid,\n partitionId: blobName,\n lastModifiedTimeInMs:\n blob.properties.lastModified && blob.properties.lastModified.getTime(),\n etag: blob.properties.etag,\n };\n partitionOwnershipArray.push(partitionOwnership);\n }\n return partitionOwnershipArray;\n } catch (err: any) {\n logger.warning(`Error occurred while fetching the list of blobs`, err.message);\n logErrorStackTrace(err);\n\n if (err?.name === \"AbortError\") throw err;\n\n throw new Error(`Error occurred while fetching the list of blobs. \\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(\n partitionOwnership: PartitionOwnership[],\n options: OperationOptions = {},\n ): Promise<PartitionOwnership[]> {\n const partitionOwnershipArray: PartitionOwnership[] = [];\n for (const ownership of partitionOwnership) {\n const blobName = BlobCheckpointStore.getBlobPrefix({ type: \"ownership\", ...ownership });\n try {\n const updatedBlobResponse = await this._setBlobMetadata(\n blobName,\n {\n ownerid: ownership.ownerId,\n },\n ownership.etag,\n options,\n );\n\n if (updatedBlobResponse.lastModified) {\n ownership.lastModifiedTimeInMs = updatedBlobResponse.lastModified.getTime();\n }\n\n ownership.etag = updatedBlobResponse.etag;\n partitionOwnershipArray.push(ownership);\n logger.info(\n `[${ownership.ownerId}] Claimed ownership successfully for partition: ${ownership.partitionId}`,\n `LastModifiedTime: ${ownership.lastModifiedTimeInMs}, ETag: ${ownership.etag}`,\n );\n } catch (err: any) {\n const restError = err as RestError;\n\n if (restError.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\n logger.warning(\n `Error occurred while claiming ownership for partition: ${ownership.partitionId}`,\n err.message,\n );\n logErrorStackTrace(err);\n\n throw 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 options: OperationOptions = {},\n ): Promise<Checkpoint[]> {\n const { abortSignal, tracingOptions } = options;\n const blobPrefix = BlobCheckpointStore.getBlobPrefix({\n type: \"checkpoint\",\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup,\n });\n\n const blobs = this._containerClient.listBlobsFlat({\n abortSignal,\n includeMetadata: true,\n prefix: blobPrefix,\n tracingOptions,\n });\n\n const checkpoints: Checkpoint[] = [];\n\n for await (const blob of blobs) {\n const blobPath = blob.name.split(\"/\");\n const blobName = blobPath[blobPath.length - 1];\n\n const checkpointMetadata = (blob.metadata as CheckpointMetadata) ?? {};\n\n const offset = checkpointMetadata.offset;\n if (offset == null) {\n throw new Error(`Missing metadata property 'offset' on blob '${blob.name}'`);\n }\n const sequenceNumber = parseIntOrThrow(\n blob.name,\n \"sequencenumber\",\n checkpointMetadata.sequencenumber,\n );\n\n checkpoints.push({\n consumerGroup,\n eventHubName,\n fullyQualifiedNamespace,\n partitionId: blobName,\n offset,\n sequenceNumber,\n });\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 The new etag on successful update.\n */\n async updateCheckpoint(checkpoint: Checkpoint, options: OperationOptions = {}): Promise<void> {\n throwTypeErrorIfParameterMissing(\n \"updateCheckpoint\",\n \"sequenceNumber\",\n checkpoint.sequenceNumber,\n );\n throwTypeErrorIfParameterMissing(\"updateCheckpoint\", \"offset\", checkpoint.offset);\n\n const blobName = BlobCheckpointStore.getBlobPrefix({ type: \"checkpoint\", ...checkpoint });\n try {\n const metadataResponse = await this._setBlobMetadata(\n blobName,\n {\n sequencenumber: checkpoint.sequenceNumber.toString(),\n offset: checkpoint.offset.toString(),\n },\n undefined,\n options,\n );\n\n logger.verbose(\n `Updated checkpoint successfully for partition: ${checkpoint.partitionId}`,\n `LastModifiedTime: ${metadataResponse.lastModified!.toISOString()}, ETag: ${\n metadataResponse.etag\n }`,\n );\n return;\n } catch (err: any) {\n logger.warning(\n `Error occurred while updating the checkpoint for partition: ${checkpoint.partitionId}.`,\n err.message,\n );\n logErrorStackTrace(err);\n\n if (err?.name === \"AbortError\") throw err;\n\n throw err;\n }\n }\n\n private static getBlobPrefix(params: {\n type: \"ownership\" | \"checkpoint\";\n fullyQualifiedNamespace: string;\n eventHubName: string;\n consumerGroup: string;\n partitionId?: string;\n }): string {\n // none of these are case-sensitive in eventhubs so we need to make sure we don't accidentally allow\n // the user to create a case-sensitive blob for their state!\n const consumerGroupName = params.consumerGroup.toLowerCase();\n const eventHubName = params.eventHubName.toLowerCase();\n const fullyQualifiedNamespace = params.fullyQualifiedNamespace.toLowerCase();\n\n if (params.partitionId) {\n return `${fullyQualifiedNamespace}/${eventHubName}/${consumerGroupName}/${params.type}/${params.partitionId}`;\n } else {\n return `${fullyQualifiedNamespace}/${eventHubName}/${consumerGroupName}/${params.type}/`;\n }\n }\n\n private async _setBlobMetadata(\n blobName: string,\n metadata: OwnershipMetadata | CheckpointMetadata,\n etag: string | undefined,\n options: OperationOptions = {},\n ): Promise<BlobSetMetadataResponse> {\n const { abortSignal, tracingOptions } = options;\n const blockBlobClient = this._containerClient.getBlobClient(blobName).getBlockBlobClient();\n\n // When we have an etag, we know the blob existed.\n // If we encounter an error we should fail.\n if (etag) {\n return blockBlobClient.setMetadata(metadata as Metadata, {\n abortSignal,\n conditions: {\n ifMatch: etag,\n },\n tracingOptions,\n });\n } else {\n try {\n // Attempt to set metadata, and fallback to upload if the blob doesn't already exist.\n // This avoids poor performance in storage accounts with soft-delete or blob versioning enabled.\n // https://github.com/Azure/azure-sdk-for-js/issues/10132\n return await blockBlobClient.setMetadata(metadata as Metadata, {\n abortSignal,\n tracingOptions,\n });\n } catch (err: any) {\n // Check if the error is `BlobNotFound` and fallback to `upload` if it is.\n if (err?.name !== \"RestError\") {\n throw err;\n }\n const errorDetails = (err as RestError).details as { [field: string]: string } | undefined;\n const errorCode = errorDetails?.errorCode;\n if (!errorCode || errorCode !== \"BlobNotFound\") {\n throw err;\n }\n\n return blockBlobClient.upload(\"\", 0, {\n abortSignal,\n metadata: metadata as Metadata,\n tracingOptions,\n });\n }\n }\n }\n}\n\ntype OwnershipMetadata = {\n [k in \"ownerid\"]: string | undefined;\n};\n\ntype CheckpointMetadata = {\n [k in \"sequencenumber\" | \"offset\"]: string | undefined;\n};\n\n/**\n * @internal\n */\nexport function parseIntOrThrow(\n blobName: string,\n fieldName: string,\n numStr: string | undefined,\n): number {\n if (numStr == null) {\n throw new Error(`Missing metadata property '${fieldName}' on blob '${blobName}'`);\n }\n\n const num = parseInt(numStr, 10);\n\n if (isNaN(num)) {\n throw new Error(\n `Failed to parse metadata property '${fieldName}' on blob '${blobName}' as a number`,\n );\n }\n\n return num;\n}\n"]}
@@ -4,13 +4,13 @@
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.BlobCheckpointStore = void 0;
6
6
  exports.parseIntOrThrow = parseIntOrThrow;
7
- const tslib_1 = require("tslib");
8
7
  const log_js_1 = require("./log.js");
9
8
  const error_js_1 = require("./util/error.js");
10
9
  /**
11
10
  * An implementation of CheckpointStore that uses Azure Blob Storage to persist checkpoint data.
12
11
  */
13
12
  class BlobCheckpointStore {
13
+ _containerClient;
14
14
  /**
15
15
  * Constructs a new instance of {@link BlobCheckpointStore}
16
16
  * @param containerClient - An instance of a storage blob ContainerClient.
@@ -33,8 +33,6 @@ class BlobCheckpointStore {
33
33
  * @returns Partition ownership details of all the partitions that have had an owner.
34
34
  */
35
35
  async listOwnership(fullyQualifiedNamespace, eventHubName, consumerGroup, options = {}) {
36
- var _a, e_1, _b, _c;
37
- var _d;
38
36
  const partitionOwnershipArray = [];
39
37
  const { abortSignal, tracingOptions } = options;
40
38
  const blobPrefix = BlobCheckpointStore.getBlobPrefix({
@@ -50,42 +48,30 @@ class BlobCheckpointStore {
50
48
  prefix: blobPrefix,
51
49
  tracingOptions,
52
50
  });
53
- try {
54
- for (var _e = true, blobs_1 = tslib_1.__asyncValues(blobs), blobs_1_1; blobs_1_1 = await blobs_1.next(), _a = blobs_1_1.done, !_a; _e = true) {
55
- _c = blobs_1_1.value;
56
- _e = false;
57
- const blob = _c;
58
- const blobPath = blob.name.split("/");
59
- const blobName = blobPath[blobPath.length - 1];
60
- const ownershipMetadata = (_d = blob.metadata) !== null && _d !== void 0 ? _d : {};
61
- if (ownershipMetadata.ownerid == null) {
62
- throw new Error(`Missing ownerid in metadata for blob ${blob.name}`);
63
- }
64
- const partitionOwnership = {
65
- fullyQualifiedNamespace,
66
- eventHubName,
67
- consumerGroup: consumerGroup,
68
- ownerId: ownershipMetadata.ownerid,
69
- partitionId: blobName,
70
- lastModifiedTimeInMs: blob.properties.lastModified && blob.properties.lastModified.getTime(),
71
- etag: blob.properties.etag,
72
- };
73
- partitionOwnershipArray.push(partitionOwnership);
74
- }
75
- }
76
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
77
- finally {
78
- try {
79
- if (!_e && !_a && (_b = blobs_1.return)) await _b.call(blobs_1);
51
+ for await (const blob of blobs) {
52
+ const blobPath = blob.name.split("/");
53
+ const blobName = blobPath[blobPath.length - 1];
54
+ const ownershipMetadata = blob.metadata ?? {};
55
+ if (ownershipMetadata.ownerid == null) {
56
+ throw new Error(`Missing ownerid in metadata for blob ${blob.name}`);
80
57
  }
81
- finally { if (e_1) throw e_1.error; }
58
+ const partitionOwnership = {
59
+ fullyQualifiedNamespace,
60
+ eventHubName,
61
+ consumerGroup: consumerGroup,
62
+ ownerId: ownershipMetadata.ownerid,
63
+ partitionId: blobName,
64
+ lastModifiedTimeInMs: blob.properties.lastModified && blob.properties.lastModified.getTime(),
65
+ etag: blob.properties.etag,
66
+ };
67
+ partitionOwnershipArray.push(partitionOwnership);
82
68
  }
83
69
  return partitionOwnershipArray;
84
70
  }
85
71
  catch (err) {
86
72
  log_js_1.logger.warning(`Error occurred while fetching the list of blobs`, err.message);
87
73
  (0, log_js_1.logErrorStackTrace)(err);
88
- if ((err === null || err === void 0 ? void 0 : err.name) === "AbortError")
74
+ if (err?.name === "AbortError")
89
75
  throw err;
90
76
  throw new Error(`Error occurred while fetching the list of blobs. \n${err}`);
91
77
  }
@@ -103,7 +89,7 @@ class BlobCheckpointStore {
103
89
  async claimOwnership(partitionOwnership, options = {}) {
104
90
  const partitionOwnershipArray = [];
105
91
  for (const ownership of partitionOwnership) {
106
- const blobName = BlobCheckpointStore.getBlobPrefix(Object.assign({ type: "ownership" }, ownership));
92
+ const blobName = BlobCheckpointStore.getBlobPrefix({ type: "ownership", ...ownership });
107
93
  try {
108
94
  const updatedBlobResponse = await this._setBlobMetadata(blobName, {
109
95
  ownerid: ownership.ownerId,
@@ -143,8 +129,6 @@ class BlobCheckpointStore {
143
129
  * - `tracingOptions`: Options for configuring tracing.
144
130
  */
145
131
  async listCheckpoints(fullyQualifiedNamespace, eventHubName, consumerGroup, options = {}) {
146
- var _a, e_2, _b, _c;
147
- var _d;
148
132
  const { abortSignal, tracingOptions } = options;
149
133
  const blobPrefix = BlobCheckpointStore.getBlobPrefix({
150
134
  type: "checkpoint",
@@ -159,35 +143,23 @@ class BlobCheckpointStore {
159
143
  tracingOptions,
160
144
  });
161
145
  const checkpoints = [];
162
- try {
163
- for (var _e = true, blobs_2 = tslib_1.__asyncValues(blobs), blobs_2_1; blobs_2_1 = await blobs_2.next(), _a = blobs_2_1.done, !_a; _e = true) {
164
- _c = blobs_2_1.value;
165
- _e = false;
166
- const blob = _c;
167
- const blobPath = blob.name.split("/");
168
- const blobName = blobPath[blobPath.length - 1];
169
- const checkpointMetadata = (_d = blob.metadata) !== null && _d !== void 0 ? _d : {};
170
- const offset = checkpointMetadata.offset;
171
- if (offset == null) {
172
- throw new Error(`Missing metadata property 'offset' on blob '${blob.name}'`);
173
- }
174
- const sequenceNumber = parseIntOrThrow(blob.name, "sequencenumber", checkpointMetadata.sequencenumber);
175
- checkpoints.push({
176
- consumerGroup,
177
- eventHubName,
178
- fullyQualifiedNamespace,
179
- partitionId: blobName,
180
- offset,
181
- sequenceNumber,
182
- });
146
+ for await (const blob of blobs) {
147
+ const blobPath = blob.name.split("/");
148
+ const blobName = blobPath[blobPath.length - 1];
149
+ const checkpointMetadata = blob.metadata ?? {};
150
+ const offset = checkpointMetadata.offset;
151
+ if (offset == null) {
152
+ throw new Error(`Missing metadata property 'offset' on blob '${blob.name}'`);
183
153
  }
184
- }
185
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
186
- finally {
187
- try {
188
- if (!_e && !_a && (_b = blobs_2.return)) await _b.call(blobs_2);
189
- }
190
- finally { if (e_2) throw e_2.error; }
154
+ const sequenceNumber = parseIntOrThrow(blob.name, "sequencenumber", checkpointMetadata.sequencenumber);
155
+ checkpoints.push({
156
+ consumerGroup,
157
+ eventHubName,
158
+ fullyQualifiedNamespace,
159
+ partitionId: blobName,
160
+ offset,
161
+ sequenceNumber,
162
+ });
191
163
  }
192
164
  return checkpoints;
193
165
  }
@@ -203,7 +175,7 @@ class BlobCheckpointStore {
203
175
  async updateCheckpoint(checkpoint, options = {}) {
204
176
  (0, error_js_1.throwTypeErrorIfParameterMissing)("updateCheckpoint", "sequenceNumber", checkpoint.sequenceNumber);
205
177
  (0, error_js_1.throwTypeErrorIfParameterMissing)("updateCheckpoint", "offset", checkpoint.offset);
206
- const blobName = BlobCheckpointStore.getBlobPrefix(Object.assign({ type: "checkpoint" }, checkpoint));
178
+ const blobName = BlobCheckpointStore.getBlobPrefix({ type: "checkpoint", ...checkpoint });
207
179
  try {
208
180
  const metadataResponse = await this._setBlobMetadata(blobName, {
209
181
  sequencenumber: checkpoint.sequenceNumber.toString(),
@@ -215,7 +187,7 @@ class BlobCheckpointStore {
215
187
  catch (err) {
216
188
  log_js_1.logger.warning(`Error occurred while updating the checkpoint for partition: ${checkpoint.partitionId}.`, err.message);
217
189
  (0, log_js_1.logErrorStackTrace)(err);
218
- if ((err === null || err === void 0 ? void 0 : err.name) === "AbortError")
190
+ if (err?.name === "AbortError")
219
191
  throw err;
220
192
  throw err;
221
193
  }
@@ -259,11 +231,11 @@ class BlobCheckpointStore {
259
231
  }
260
232
  catch (err) {
261
233
  // Check if the error is `BlobNotFound` and fallback to `upload` if it is.
262
- if ((err === null || err === void 0 ? void 0 : err.name) !== "RestError") {
234
+ if (err?.name !== "RestError") {
263
235
  throw err;
264
236
  }
265
237
  const errorDetails = err.details;
266
- const errorCode = errorDetails === null || errorDetails === void 0 ? void 0 : errorDetails.errorCode;
238
+ const errorCode = errorDetails?.errorCode;
267
239
  if (!errorCode || errorCode !== "BlobNotFound") {
268
240
  throw err;
269
241
  }
@@ -1 +1 @@
1
- {"version":3,"file":"blobCheckpointStore.js","sourceRoot":"","sources":["../../src/blobCheckpointStore.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n CheckpointStore,\n PartitionOwnership,\n Checkpoint,\n OperationOptions,\n} from \"@azure/event-hubs\";\nimport type { Metadata, RestError, BlobSetMetadataResponse } from \"@azure/storage-blob\";\nimport { logger, logErrorStackTrace } from \"./log.js\";\nimport type { ContainerClientLike } from \"./storageBlobInterfaces.js\";\nimport { throwTypeErrorIfParameterMissing } from \"./util/error.js\";\n\n/**\n * An implementation of CheckpointStore that uses Azure Blob Storage to persist checkpoint data.\n */\nexport class BlobCheckpointStore implements CheckpointStore {\n private _containerClient: ContainerClientLike;\n\n /**\n * Constructs a new instance of {@link BlobCheckpointStore}\n * @param containerClient - An instance of a storage blob ContainerClient.\n */\n constructor(containerClient: ContainerClientLike) {\n this._containerClient = containerClient;\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 options: OperationOptions = {},\n ): Promise<PartitionOwnership[]> {\n const partitionOwnershipArray: PartitionOwnership[] = [];\n const { abortSignal, tracingOptions } = options;\n\n const blobPrefix = BlobCheckpointStore.getBlobPrefix({\n type: \"ownership\",\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup: consumerGroup,\n });\n\n try {\n const blobs = this._containerClient.listBlobsFlat({\n abortSignal,\n includeMetadata: true,\n prefix: blobPrefix,\n tracingOptions,\n });\n\n for await (const blob of blobs) {\n const blobPath = blob.name.split(\"/\");\n const blobName = blobPath[blobPath.length - 1];\n\n const ownershipMetadata = (blob.metadata as OwnershipMetadata) ?? {};\n\n if (ownershipMetadata.ownerid == null) {\n throw new Error(`Missing ownerid in metadata for blob ${blob.name}`);\n }\n\n const partitionOwnership: PartitionOwnership = {\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup: consumerGroup,\n ownerId: ownershipMetadata.ownerid,\n partitionId: blobName,\n lastModifiedTimeInMs:\n blob.properties.lastModified && blob.properties.lastModified.getTime(),\n etag: blob.properties.etag,\n };\n partitionOwnershipArray.push(partitionOwnership);\n }\n return partitionOwnershipArray;\n } catch (err: any) {\n logger.warning(`Error occurred while fetching the list of blobs`, err.message);\n logErrorStackTrace(err);\n\n if (err?.name === \"AbortError\") throw err;\n\n throw new Error(`Error occurred while fetching the list of blobs. \\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(\n partitionOwnership: PartitionOwnership[],\n options: OperationOptions = {},\n ): Promise<PartitionOwnership[]> {\n const partitionOwnershipArray: PartitionOwnership[] = [];\n for (const ownership of partitionOwnership) {\n const blobName = BlobCheckpointStore.getBlobPrefix({ type: \"ownership\", ...ownership });\n try {\n const updatedBlobResponse = await this._setBlobMetadata(\n blobName,\n {\n ownerid: ownership.ownerId,\n },\n ownership.etag,\n options,\n );\n\n if (updatedBlobResponse.lastModified) {\n ownership.lastModifiedTimeInMs = updatedBlobResponse.lastModified.getTime();\n }\n\n ownership.etag = updatedBlobResponse.etag;\n partitionOwnershipArray.push(ownership);\n logger.info(\n `[${ownership.ownerId}] Claimed ownership successfully for partition: ${ownership.partitionId}`,\n `LastModifiedTime: ${ownership.lastModifiedTimeInMs}, ETag: ${ownership.etag}`,\n );\n } catch (err: any) {\n const restError = err as RestError;\n\n if (restError.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\n logger.warning(\n `Error occurred while claiming ownership for partition: ${ownership.partitionId}`,\n err.message,\n );\n logErrorStackTrace(err);\n\n throw 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 options: OperationOptions = {},\n ): Promise<Checkpoint[]> {\n const { abortSignal, tracingOptions } = options;\n const blobPrefix = BlobCheckpointStore.getBlobPrefix({\n type: \"checkpoint\",\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup,\n });\n\n const blobs = this._containerClient.listBlobsFlat({\n abortSignal,\n includeMetadata: true,\n prefix: blobPrefix,\n tracingOptions,\n });\n\n const checkpoints: Checkpoint[] = [];\n\n for await (const blob of blobs) {\n const blobPath = blob.name.split(\"/\");\n const blobName = blobPath[blobPath.length - 1];\n\n const checkpointMetadata = (blob.metadata as CheckpointMetadata) ?? {};\n\n const offset = checkpointMetadata.offset;\n if (offset == null) {\n throw new Error(`Missing metadata property 'offset' on blob '${blob.name}'`);\n }\n const sequenceNumber = parseIntOrThrow(\n blob.name,\n \"sequencenumber\",\n checkpointMetadata.sequencenumber,\n );\n\n checkpoints.push({\n consumerGroup,\n eventHubName,\n fullyQualifiedNamespace,\n partitionId: blobName,\n offset,\n sequenceNumber,\n });\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 The new etag on successful update.\n */\n async updateCheckpoint(checkpoint: Checkpoint, options: OperationOptions = {}): Promise<void> {\n throwTypeErrorIfParameterMissing(\n \"updateCheckpoint\",\n \"sequenceNumber\",\n checkpoint.sequenceNumber,\n );\n throwTypeErrorIfParameterMissing(\"updateCheckpoint\", \"offset\", checkpoint.offset);\n\n const blobName = BlobCheckpointStore.getBlobPrefix({ type: \"checkpoint\", ...checkpoint });\n try {\n const metadataResponse = await this._setBlobMetadata(\n blobName,\n {\n sequencenumber: checkpoint.sequenceNumber.toString(),\n offset: checkpoint.offset.toString(),\n },\n undefined,\n options,\n );\n\n logger.verbose(\n `Updated checkpoint successfully for partition: ${checkpoint.partitionId}`,\n `LastModifiedTime: ${metadataResponse.lastModified!.toISOString()}, ETag: ${\n metadataResponse.etag\n }`,\n );\n return;\n } catch (err: any) {\n logger.warning(\n `Error occurred while updating the checkpoint for partition: ${checkpoint.partitionId}.`,\n err.message,\n );\n logErrorStackTrace(err);\n\n if (err?.name === \"AbortError\") throw err;\n\n throw err;\n }\n }\n\n private static getBlobPrefix(params: {\n type: \"ownership\" | \"checkpoint\";\n fullyQualifiedNamespace: string;\n eventHubName: string;\n consumerGroup: string;\n partitionId?: string;\n }): string {\n // none of these are case-sensitive in eventhubs so we need to make sure we don't accidentally allow\n // the user to create a case-sensitive blob for their state!\n const consumerGroupName = params.consumerGroup.toLowerCase();\n const eventHubName = params.eventHubName.toLowerCase();\n const fullyQualifiedNamespace = params.fullyQualifiedNamespace.toLowerCase();\n\n if (params.partitionId) {\n return `${fullyQualifiedNamespace}/${eventHubName}/${consumerGroupName}/${params.type}/${params.partitionId}`;\n } else {\n return `${fullyQualifiedNamespace}/${eventHubName}/${consumerGroupName}/${params.type}/`;\n }\n }\n\n private async _setBlobMetadata(\n blobName: string,\n metadata: OwnershipMetadata | CheckpointMetadata,\n etag: string | undefined,\n options: OperationOptions = {},\n ): Promise<BlobSetMetadataResponse> {\n const { abortSignal, tracingOptions } = options;\n const blockBlobClient = this._containerClient.getBlobClient(blobName).getBlockBlobClient();\n\n // When we have an etag, we know the blob existed.\n // If we encounter an error we should fail.\n if (etag) {\n return blockBlobClient.setMetadata(metadata as Metadata, {\n abortSignal,\n conditions: {\n ifMatch: etag,\n },\n tracingOptions,\n });\n } else {\n try {\n // Attempt to set metadata, and fallback to upload if the blob doesn't already exist.\n // This avoids poor performance in storage accounts with soft-delete or blob versioning enabled.\n // https://github.com/Azure/azure-sdk-for-js/issues/10132\n return await blockBlobClient.setMetadata(metadata as Metadata, {\n abortSignal,\n tracingOptions,\n });\n } catch (err: any) {\n // Check if the error is `BlobNotFound` and fallback to `upload` if it is.\n if (err?.name !== \"RestError\") {\n throw err;\n }\n const errorDetails = (err as RestError).details as { [field: string]: string } | undefined;\n const errorCode = errorDetails?.errorCode;\n if (!errorCode || errorCode !== \"BlobNotFound\") {\n throw err;\n }\n\n return blockBlobClient.upload(\"\", 0, {\n abortSignal,\n metadata: metadata as Metadata,\n tracingOptions,\n });\n }\n }\n }\n}\n\ntype OwnershipMetadata = {\n [k in \"ownerid\"]: string | undefined;\n};\n\ntype CheckpointMetadata = {\n [k in \"sequencenumber\" | \"offset\"]: string | undefined;\n};\n\n/**\n * @internal\n */\nexport function parseIntOrThrow(\n blobName: string,\n fieldName: string,\n numStr: string | undefined,\n): number {\n if (numStr == null) {\n throw new Error(`Missing metadata property '${fieldName}' on blob '${blobName}'`);\n }\n\n const num = parseInt(numStr, 10);\n\n if (isNaN(num)) {\n throw new Error(\n `Failed to parse metadata property '${fieldName}' on blob '${blobName}' as a number`,\n );\n }\n\n return num;\n}\n"]}
1
+ {"version":3,"file":"blobCheckpointStore.js","sourceRoot":"","sources":["../../src/blobCheckpointStore.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n CheckpointStore,\n PartitionOwnership,\n Checkpoint,\n OperationOptions,\n} from \"@azure/event-hubs\";\nimport type { Metadata, RestError, BlobSetMetadataResponse } from \"@azure/storage-blob\";\nimport { logger, logErrorStackTrace } from \"./log.js\";\nimport type { ContainerClientLike } from \"./storageBlobInterfaces.js\";\nimport { throwTypeErrorIfParameterMissing } from \"./util/error.js\";\n\n/**\n * An implementation of CheckpointStore that uses Azure Blob Storage to persist checkpoint data.\n */\nexport class BlobCheckpointStore implements CheckpointStore {\n private _containerClient: ContainerClientLike;\n\n /**\n * Constructs a new instance of {@link BlobCheckpointStore}\n * @param containerClient - An instance of a storage blob ContainerClient.\n */\n constructor(containerClient: ContainerClientLike) {\n this._containerClient = containerClient;\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 options: OperationOptions = {},\n ): Promise<PartitionOwnership[]> {\n const partitionOwnershipArray: PartitionOwnership[] = [];\n const { abortSignal, tracingOptions } = options;\n\n const blobPrefix = BlobCheckpointStore.getBlobPrefix({\n type: \"ownership\",\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup: consumerGroup,\n });\n\n try {\n const blobs = this._containerClient.listBlobsFlat({\n abortSignal,\n includeMetadata: true,\n prefix: blobPrefix,\n tracingOptions,\n });\n\n for await (const blob of blobs) {\n const blobPath = blob.name.split(\"/\");\n const blobName = blobPath[blobPath.length - 1];\n\n const ownershipMetadata = (blob.metadata as OwnershipMetadata) ?? {};\n\n if (ownershipMetadata.ownerid == null) {\n throw new Error(`Missing ownerid in metadata for blob ${blob.name}`);\n }\n\n const partitionOwnership: PartitionOwnership = {\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup: consumerGroup,\n ownerId: ownershipMetadata.ownerid,\n partitionId: blobName,\n lastModifiedTimeInMs:\n blob.properties.lastModified && blob.properties.lastModified.getTime(),\n etag: blob.properties.etag,\n };\n partitionOwnershipArray.push(partitionOwnership);\n }\n return partitionOwnershipArray;\n } catch (err: any) {\n logger.warning(`Error occurred while fetching the list of blobs`, err.message);\n logErrorStackTrace(err);\n\n if (err?.name === \"AbortError\") throw err;\n\n throw new Error(`Error occurred while fetching the list of blobs. \\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(\n partitionOwnership: PartitionOwnership[],\n options: OperationOptions = {},\n ): Promise<PartitionOwnership[]> {\n const partitionOwnershipArray: PartitionOwnership[] = [];\n for (const ownership of partitionOwnership) {\n const blobName = BlobCheckpointStore.getBlobPrefix({ type: \"ownership\", ...ownership });\n try {\n const updatedBlobResponse = await this._setBlobMetadata(\n blobName,\n {\n ownerid: ownership.ownerId,\n },\n ownership.etag,\n options,\n );\n\n if (updatedBlobResponse.lastModified) {\n ownership.lastModifiedTimeInMs = updatedBlobResponse.lastModified.getTime();\n }\n\n ownership.etag = updatedBlobResponse.etag;\n partitionOwnershipArray.push(ownership);\n logger.info(\n `[${ownership.ownerId}] Claimed ownership successfully for partition: ${ownership.partitionId}`,\n `LastModifiedTime: ${ownership.lastModifiedTimeInMs}, ETag: ${ownership.etag}`,\n );\n } catch (err: any) {\n const restError = err as RestError;\n\n if (restError.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\n logger.warning(\n `Error occurred while claiming ownership for partition: ${ownership.partitionId}`,\n err.message,\n );\n logErrorStackTrace(err);\n\n throw 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 options: OperationOptions = {},\n ): Promise<Checkpoint[]> {\n const { abortSignal, tracingOptions } = options;\n const blobPrefix = BlobCheckpointStore.getBlobPrefix({\n type: \"checkpoint\",\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup,\n });\n\n const blobs = this._containerClient.listBlobsFlat({\n abortSignal,\n includeMetadata: true,\n prefix: blobPrefix,\n tracingOptions,\n });\n\n const checkpoints: Checkpoint[] = [];\n\n for await (const blob of blobs) {\n const blobPath = blob.name.split(\"/\");\n const blobName = blobPath[blobPath.length - 1];\n\n const checkpointMetadata = (blob.metadata as CheckpointMetadata) ?? {};\n\n const offset = checkpointMetadata.offset;\n if (offset == null) {\n throw new Error(`Missing metadata property 'offset' on blob '${blob.name}'`);\n }\n const sequenceNumber = parseIntOrThrow(\n blob.name,\n \"sequencenumber\",\n checkpointMetadata.sequencenumber,\n );\n\n checkpoints.push({\n consumerGroup,\n eventHubName,\n fullyQualifiedNamespace,\n partitionId: blobName,\n offset,\n sequenceNumber,\n });\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 The new etag on successful update.\n */\n async updateCheckpoint(checkpoint: Checkpoint, options: OperationOptions = {}): Promise<void> {\n throwTypeErrorIfParameterMissing(\n \"updateCheckpoint\",\n \"sequenceNumber\",\n checkpoint.sequenceNumber,\n );\n throwTypeErrorIfParameterMissing(\"updateCheckpoint\", \"offset\", checkpoint.offset);\n\n const blobName = BlobCheckpointStore.getBlobPrefix({ type: \"checkpoint\", ...checkpoint });\n try {\n const metadataResponse = await this._setBlobMetadata(\n blobName,\n {\n sequencenumber: checkpoint.sequenceNumber.toString(),\n offset: checkpoint.offset.toString(),\n },\n undefined,\n options,\n );\n\n logger.verbose(\n `Updated checkpoint successfully for partition: ${checkpoint.partitionId}`,\n `LastModifiedTime: ${metadataResponse.lastModified!.toISOString()}, ETag: ${\n metadataResponse.etag\n }`,\n );\n return;\n } catch (err: any) {\n logger.warning(\n `Error occurred while updating the checkpoint for partition: ${checkpoint.partitionId}.`,\n err.message,\n );\n logErrorStackTrace(err);\n\n if (err?.name === \"AbortError\") throw err;\n\n throw err;\n }\n }\n\n private static getBlobPrefix(params: {\n type: \"ownership\" | \"checkpoint\";\n fullyQualifiedNamespace: string;\n eventHubName: string;\n consumerGroup: string;\n partitionId?: string;\n }): string {\n // none of these are case-sensitive in eventhubs so we need to make sure we don't accidentally allow\n // the user to create a case-sensitive blob for their state!\n const consumerGroupName = params.consumerGroup.toLowerCase();\n const eventHubName = params.eventHubName.toLowerCase();\n const fullyQualifiedNamespace = params.fullyQualifiedNamespace.toLowerCase();\n\n if (params.partitionId) {\n return `${fullyQualifiedNamespace}/${eventHubName}/${consumerGroupName}/${params.type}/${params.partitionId}`;\n } else {\n return `${fullyQualifiedNamespace}/${eventHubName}/${consumerGroupName}/${params.type}/`;\n }\n }\n\n private async _setBlobMetadata(\n blobName: string,\n metadata: OwnershipMetadata | CheckpointMetadata,\n etag: string | undefined,\n options: OperationOptions = {},\n ): Promise<BlobSetMetadataResponse> {\n const { abortSignal, tracingOptions } = options;\n const blockBlobClient = this._containerClient.getBlobClient(blobName).getBlockBlobClient();\n\n // When we have an etag, we know the blob existed.\n // If we encounter an error we should fail.\n if (etag) {\n return blockBlobClient.setMetadata(metadata as Metadata, {\n abortSignal,\n conditions: {\n ifMatch: etag,\n },\n tracingOptions,\n });\n } else {\n try {\n // Attempt to set metadata, and fallback to upload if the blob doesn't already exist.\n // This avoids poor performance in storage accounts with soft-delete or blob versioning enabled.\n // https://github.com/Azure/azure-sdk-for-js/issues/10132\n return await blockBlobClient.setMetadata(metadata as Metadata, {\n abortSignal,\n tracingOptions,\n });\n } catch (err: any) {\n // Check if the error is `BlobNotFound` and fallback to `upload` if it is.\n if (err?.name !== \"RestError\") {\n throw err;\n }\n const errorDetails = (err as RestError).details as { [field: string]: string } | undefined;\n const errorCode = errorDetails?.errorCode;\n if (!errorCode || errorCode !== \"BlobNotFound\") {\n throw err;\n }\n\n return blockBlobClient.upload(\"\", 0, {\n abortSignal,\n metadata: metadata as Metadata,\n tracingOptions,\n });\n }\n }\n }\n}\n\ntype OwnershipMetadata = {\n [k in \"ownerid\"]: string | undefined;\n};\n\ntype CheckpointMetadata = {\n [k in \"sequencenumber\" | \"offset\"]: string | undefined;\n};\n\n/**\n * @internal\n */\nexport function parseIntOrThrow(\n blobName: string,\n fieldName: string,\n numStr: string | undefined,\n): number {\n if (numStr == null) {\n throw new Error(`Missing metadata property '${fieldName}' on blob '${blobName}'`);\n }\n\n const num = parseInt(numStr, 10);\n\n if (isNaN(num)) {\n throw new Error(\n `Failed to parse metadata property '${fieldName}' on blob '${blobName}' as a number`,\n );\n }\n\n return num;\n}\n"]}
@@ -1,11 +1,11 @@
1
- // This file is read by tools that parse documentation comments conforming to the TSDoc standard.
2
- // It should be published with your NPM package. It should not be tracked by Git.
3
- {
4
- "tsdocVersion": "0.12",
5
- "toolPackages": [
6
- {
7
- "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.52.8"
9
- }
10
- ]
11
- }
1
+ // This file is read by tools that parse documentation comments conforming to the TSDoc standard.
2
+ // It should be published with your NPM package. It should not be tracked by Git.
3
+ {
4
+ "tsdocVersion": "0.12",
5
+ "toolPackages": [
6
+ {
7
+ "packageName": "@microsoft/api-extractor",
8
+ "packageVersion": "7.52.9"
9
+ }
10
+ ]
11
+ }
@@ -1,12 +1,12 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
- import { __asyncValues } from "tslib";
4
3
  import { logger, logErrorStackTrace } from "./log.js";
5
4
  import { throwTypeErrorIfParameterMissing } from "./util/error.js";
6
5
  /**
7
6
  * An implementation of CheckpointStore that uses Azure Blob Storage to persist checkpoint data.
8
7
  */
9
8
  export class BlobCheckpointStore {
9
+ _containerClient;
10
10
  /**
11
11
  * Constructs a new instance of {@link BlobCheckpointStore}
12
12
  * @param containerClient - An instance of a storage blob ContainerClient.
@@ -29,8 +29,6 @@ export class BlobCheckpointStore {
29
29
  * @returns Partition ownership details of all the partitions that have had an owner.
30
30
  */
31
31
  async listOwnership(fullyQualifiedNamespace, eventHubName, consumerGroup, options = {}) {
32
- var _a, e_1, _b, _c;
33
- var _d;
34
32
  const partitionOwnershipArray = [];
35
33
  const { abortSignal, tracingOptions } = options;
36
34
  const blobPrefix = BlobCheckpointStore.getBlobPrefix({
@@ -46,42 +44,30 @@ export class BlobCheckpointStore {
46
44
  prefix: blobPrefix,
47
45
  tracingOptions,
48
46
  });
49
- try {
50
- for (var _e = true, blobs_1 = __asyncValues(blobs), blobs_1_1; blobs_1_1 = await blobs_1.next(), _a = blobs_1_1.done, !_a; _e = true) {
51
- _c = blobs_1_1.value;
52
- _e = false;
53
- const blob = _c;
54
- const blobPath = blob.name.split("/");
55
- const blobName = blobPath[blobPath.length - 1];
56
- const ownershipMetadata = (_d = blob.metadata) !== null && _d !== void 0 ? _d : {};
57
- if (ownershipMetadata.ownerid == null) {
58
- throw new Error(`Missing ownerid in metadata for blob ${blob.name}`);
59
- }
60
- const partitionOwnership = {
61
- fullyQualifiedNamespace,
62
- eventHubName,
63
- consumerGroup: consumerGroup,
64
- ownerId: ownershipMetadata.ownerid,
65
- partitionId: blobName,
66
- lastModifiedTimeInMs: blob.properties.lastModified && blob.properties.lastModified.getTime(),
67
- etag: blob.properties.etag,
68
- };
69
- partitionOwnershipArray.push(partitionOwnership);
70
- }
71
- }
72
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
73
- finally {
74
- try {
75
- if (!_e && !_a && (_b = blobs_1.return)) await _b.call(blobs_1);
47
+ for await (const blob of blobs) {
48
+ const blobPath = blob.name.split("/");
49
+ const blobName = blobPath[blobPath.length - 1];
50
+ const ownershipMetadata = blob.metadata ?? {};
51
+ if (ownershipMetadata.ownerid == null) {
52
+ throw new Error(`Missing ownerid in metadata for blob ${blob.name}`);
76
53
  }
77
- finally { if (e_1) throw e_1.error; }
54
+ const partitionOwnership = {
55
+ fullyQualifiedNamespace,
56
+ eventHubName,
57
+ consumerGroup: consumerGroup,
58
+ ownerId: ownershipMetadata.ownerid,
59
+ partitionId: blobName,
60
+ lastModifiedTimeInMs: blob.properties.lastModified && blob.properties.lastModified.getTime(),
61
+ etag: blob.properties.etag,
62
+ };
63
+ partitionOwnershipArray.push(partitionOwnership);
78
64
  }
79
65
  return partitionOwnershipArray;
80
66
  }
81
67
  catch (err) {
82
68
  logger.warning(`Error occurred while fetching the list of blobs`, err.message);
83
69
  logErrorStackTrace(err);
84
- if ((err === null || err === void 0 ? void 0 : err.name) === "AbortError")
70
+ if (err?.name === "AbortError")
85
71
  throw err;
86
72
  throw new Error(`Error occurred while fetching the list of blobs. \n${err}`);
87
73
  }
@@ -99,7 +85,7 @@ export class BlobCheckpointStore {
99
85
  async claimOwnership(partitionOwnership, options = {}) {
100
86
  const partitionOwnershipArray = [];
101
87
  for (const ownership of partitionOwnership) {
102
- const blobName = BlobCheckpointStore.getBlobPrefix(Object.assign({ type: "ownership" }, ownership));
88
+ const blobName = BlobCheckpointStore.getBlobPrefix({ type: "ownership", ...ownership });
103
89
  try {
104
90
  const updatedBlobResponse = await this._setBlobMetadata(blobName, {
105
91
  ownerid: ownership.ownerId,
@@ -139,8 +125,6 @@ export class BlobCheckpointStore {
139
125
  * - `tracingOptions`: Options for configuring tracing.
140
126
  */
141
127
  async listCheckpoints(fullyQualifiedNamespace, eventHubName, consumerGroup, options = {}) {
142
- var _a, e_2, _b, _c;
143
- var _d;
144
128
  const { abortSignal, tracingOptions } = options;
145
129
  const blobPrefix = BlobCheckpointStore.getBlobPrefix({
146
130
  type: "checkpoint",
@@ -155,35 +139,23 @@ export class BlobCheckpointStore {
155
139
  tracingOptions,
156
140
  });
157
141
  const checkpoints = [];
158
- try {
159
- for (var _e = true, blobs_2 = __asyncValues(blobs), blobs_2_1; blobs_2_1 = await blobs_2.next(), _a = blobs_2_1.done, !_a; _e = true) {
160
- _c = blobs_2_1.value;
161
- _e = false;
162
- const blob = _c;
163
- const blobPath = blob.name.split("/");
164
- const blobName = blobPath[blobPath.length - 1];
165
- const checkpointMetadata = (_d = blob.metadata) !== null && _d !== void 0 ? _d : {};
166
- const offset = checkpointMetadata.offset;
167
- if (offset == null) {
168
- throw new Error(`Missing metadata property 'offset' on blob '${blob.name}'`);
169
- }
170
- const sequenceNumber = parseIntOrThrow(blob.name, "sequencenumber", checkpointMetadata.sequencenumber);
171
- checkpoints.push({
172
- consumerGroup,
173
- eventHubName,
174
- fullyQualifiedNamespace,
175
- partitionId: blobName,
176
- offset,
177
- sequenceNumber,
178
- });
142
+ for await (const blob of blobs) {
143
+ const blobPath = blob.name.split("/");
144
+ const blobName = blobPath[blobPath.length - 1];
145
+ const checkpointMetadata = blob.metadata ?? {};
146
+ const offset = checkpointMetadata.offset;
147
+ if (offset == null) {
148
+ throw new Error(`Missing metadata property 'offset' on blob '${blob.name}'`);
179
149
  }
180
- }
181
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
182
- finally {
183
- try {
184
- if (!_e && !_a && (_b = blobs_2.return)) await _b.call(blobs_2);
185
- }
186
- finally { if (e_2) throw e_2.error; }
150
+ const sequenceNumber = parseIntOrThrow(blob.name, "sequencenumber", checkpointMetadata.sequencenumber);
151
+ checkpoints.push({
152
+ consumerGroup,
153
+ eventHubName,
154
+ fullyQualifiedNamespace,
155
+ partitionId: blobName,
156
+ offset,
157
+ sequenceNumber,
158
+ });
187
159
  }
188
160
  return checkpoints;
189
161
  }
@@ -199,7 +171,7 @@ export class BlobCheckpointStore {
199
171
  async updateCheckpoint(checkpoint, options = {}) {
200
172
  throwTypeErrorIfParameterMissing("updateCheckpoint", "sequenceNumber", checkpoint.sequenceNumber);
201
173
  throwTypeErrorIfParameterMissing("updateCheckpoint", "offset", checkpoint.offset);
202
- const blobName = BlobCheckpointStore.getBlobPrefix(Object.assign({ type: "checkpoint" }, checkpoint));
174
+ const blobName = BlobCheckpointStore.getBlobPrefix({ type: "checkpoint", ...checkpoint });
203
175
  try {
204
176
  const metadataResponse = await this._setBlobMetadata(blobName, {
205
177
  sequencenumber: checkpoint.sequenceNumber.toString(),
@@ -211,7 +183,7 @@ export class BlobCheckpointStore {
211
183
  catch (err) {
212
184
  logger.warning(`Error occurred while updating the checkpoint for partition: ${checkpoint.partitionId}.`, err.message);
213
185
  logErrorStackTrace(err);
214
- if ((err === null || err === void 0 ? void 0 : err.name) === "AbortError")
186
+ if (err?.name === "AbortError")
215
187
  throw err;
216
188
  throw err;
217
189
  }
@@ -255,11 +227,11 @@ export class BlobCheckpointStore {
255
227
  }
256
228
  catch (err) {
257
229
  // Check if the error is `BlobNotFound` and fallback to `upload` if it is.
258
- if ((err === null || err === void 0 ? void 0 : err.name) !== "RestError") {
230
+ if (err?.name !== "RestError") {
259
231
  throw err;
260
232
  }
261
233
  const errorDetails = err.details;
262
- const errorCode = errorDetails === null || errorDetails === void 0 ? void 0 : errorDetails.errorCode;
234
+ const errorCode = errorDetails?.errorCode;
263
235
  if (!errorCode || errorCode !== "BlobNotFound") {
264
236
  throw err;
265
237
  }
@@ -1 +1 @@
1
- {"version":3,"file":"blobCheckpointStore.js","sourceRoot":"","sources":["../../src/blobCheckpointStore.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n CheckpointStore,\n PartitionOwnership,\n Checkpoint,\n OperationOptions,\n} from \"@azure/event-hubs\";\nimport type { Metadata, RestError, BlobSetMetadataResponse } from \"@azure/storage-blob\";\nimport { logger, logErrorStackTrace } from \"./log.js\";\nimport type { ContainerClientLike } from \"./storageBlobInterfaces.js\";\nimport { throwTypeErrorIfParameterMissing } from \"./util/error.js\";\n\n/**\n * An implementation of CheckpointStore that uses Azure Blob Storage to persist checkpoint data.\n */\nexport class BlobCheckpointStore implements CheckpointStore {\n private _containerClient: ContainerClientLike;\n\n /**\n * Constructs a new instance of {@link BlobCheckpointStore}\n * @param containerClient - An instance of a storage blob ContainerClient.\n */\n constructor(containerClient: ContainerClientLike) {\n this._containerClient = containerClient;\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 options: OperationOptions = {},\n ): Promise<PartitionOwnership[]> {\n const partitionOwnershipArray: PartitionOwnership[] = [];\n const { abortSignal, tracingOptions } = options;\n\n const blobPrefix = BlobCheckpointStore.getBlobPrefix({\n type: \"ownership\",\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup: consumerGroup,\n });\n\n try {\n const blobs = this._containerClient.listBlobsFlat({\n abortSignal,\n includeMetadata: true,\n prefix: blobPrefix,\n tracingOptions,\n });\n\n for await (const blob of blobs) {\n const blobPath = blob.name.split(\"/\");\n const blobName = blobPath[blobPath.length - 1];\n\n const ownershipMetadata = (blob.metadata as OwnershipMetadata) ?? {};\n\n if (ownershipMetadata.ownerid == null) {\n throw new Error(`Missing ownerid in metadata for blob ${blob.name}`);\n }\n\n const partitionOwnership: PartitionOwnership = {\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup: consumerGroup,\n ownerId: ownershipMetadata.ownerid,\n partitionId: blobName,\n lastModifiedTimeInMs:\n blob.properties.lastModified && blob.properties.lastModified.getTime(),\n etag: blob.properties.etag,\n };\n partitionOwnershipArray.push(partitionOwnership);\n }\n return partitionOwnershipArray;\n } catch (err: any) {\n logger.warning(`Error occurred while fetching the list of blobs`, err.message);\n logErrorStackTrace(err);\n\n if (err?.name === \"AbortError\") throw err;\n\n throw new Error(`Error occurred while fetching the list of blobs. \\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(\n partitionOwnership: PartitionOwnership[],\n options: OperationOptions = {},\n ): Promise<PartitionOwnership[]> {\n const partitionOwnershipArray: PartitionOwnership[] = [];\n for (const ownership of partitionOwnership) {\n const blobName = BlobCheckpointStore.getBlobPrefix({ type: \"ownership\", ...ownership });\n try {\n const updatedBlobResponse = await this._setBlobMetadata(\n blobName,\n {\n ownerid: ownership.ownerId,\n },\n ownership.etag,\n options,\n );\n\n if (updatedBlobResponse.lastModified) {\n ownership.lastModifiedTimeInMs = updatedBlobResponse.lastModified.getTime();\n }\n\n ownership.etag = updatedBlobResponse.etag;\n partitionOwnershipArray.push(ownership);\n logger.info(\n `[${ownership.ownerId}] Claimed ownership successfully for partition: ${ownership.partitionId}`,\n `LastModifiedTime: ${ownership.lastModifiedTimeInMs}, ETag: ${ownership.etag}`,\n );\n } catch (err: any) {\n const restError = err as RestError;\n\n if (restError.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\n logger.warning(\n `Error occurred while claiming ownership for partition: ${ownership.partitionId}`,\n err.message,\n );\n logErrorStackTrace(err);\n\n throw 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 options: OperationOptions = {},\n ): Promise<Checkpoint[]> {\n const { abortSignal, tracingOptions } = options;\n const blobPrefix = BlobCheckpointStore.getBlobPrefix({\n type: \"checkpoint\",\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup,\n });\n\n const blobs = this._containerClient.listBlobsFlat({\n abortSignal,\n includeMetadata: true,\n prefix: blobPrefix,\n tracingOptions,\n });\n\n const checkpoints: Checkpoint[] = [];\n\n for await (const blob of blobs) {\n const blobPath = blob.name.split(\"/\");\n const blobName = blobPath[blobPath.length - 1];\n\n const checkpointMetadata = (blob.metadata as CheckpointMetadata) ?? {};\n\n const offset = checkpointMetadata.offset;\n if (offset == null) {\n throw new Error(`Missing metadata property 'offset' on blob '${blob.name}'`);\n }\n const sequenceNumber = parseIntOrThrow(\n blob.name,\n \"sequencenumber\",\n checkpointMetadata.sequencenumber,\n );\n\n checkpoints.push({\n consumerGroup,\n eventHubName,\n fullyQualifiedNamespace,\n partitionId: blobName,\n offset,\n sequenceNumber,\n });\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 The new etag on successful update.\n */\n async updateCheckpoint(checkpoint: Checkpoint, options: OperationOptions = {}): Promise<void> {\n throwTypeErrorIfParameterMissing(\n \"updateCheckpoint\",\n \"sequenceNumber\",\n checkpoint.sequenceNumber,\n );\n throwTypeErrorIfParameterMissing(\"updateCheckpoint\", \"offset\", checkpoint.offset);\n\n const blobName = BlobCheckpointStore.getBlobPrefix({ type: \"checkpoint\", ...checkpoint });\n try {\n const metadataResponse = await this._setBlobMetadata(\n blobName,\n {\n sequencenumber: checkpoint.sequenceNumber.toString(),\n offset: checkpoint.offset.toString(),\n },\n undefined,\n options,\n );\n\n logger.verbose(\n `Updated checkpoint successfully for partition: ${checkpoint.partitionId}`,\n `LastModifiedTime: ${metadataResponse.lastModified!.toISOString()}, ETag: ${\n metadataResponse.etag\n }`,\n );\n return;\n } catch (err: any) {\n logger.warning(\n `Error occurred while updating the checkpoint for partition: ${checkpoint.partitionId}.`,\n err.message,\n );\n logErrorStackTrace(err);\n\n if (err?.name === \"AbortError\") throw err;\n\n throw err;\n }\n }\n\n private static getBlobPrefix(params: {\n type: \"ownership\" | \"checkpoint\";\n fullyQualifiedNamespace: string;\n eventHubName: string;\n consumerGroup: string;\n partitionId?: string;\n }): string {\n // none of these are case-sensitive in eventhubs so we need to make sure we don't accidentally allow\n // the user to create a case-sensitive blob for their state!\n const consumerGroupName = params.consumerGroup.toLowerCase();\n const eventHubName = params.eventHubName.toLowerCase();\n const fullyQualifiedNamespace = params.fullyQualifiedNamespace.toLowerCase();\n\n if (params.partitionId) {\n return `${fullyQualifiedNamespace}/${eventHubName}/${consumerGroupName}/${params.type}/${params.partitionId}`;\n } else {\n return `${fullyQualifiedNamespace}/${eventHubName}/${consumerGroupName}/${params.type}/`;\n }\n }\n\n private async _setBlobMetadata(\n blobName: string,\n metadata: OwnershipMetadata | CheckpointMetadata,\n etag: string | undefined,\n options: OperationOptions = {},\n ): Promise<BlobSetMetadataResponse> {\n const { abortSignal, tracingOptions } = options;\n const blockBlobClient = this._containerClient.getBlobClient(blobName).getBlockBlobClient();\n\n // When we have an etag, we know the blob existed.\n // If we encounter an error we should fail.\n if (etag) {\n return blockBlobClient.setMetadata(metadata as Metadata, {\n abortSignal,\n conditions: {\n ifMatch: etag,\n },\n tracingOptions,\n });\n } else {\n try {\n // Attempt to set metadata, and fallback to upload if the blob doesn't already exist.\n // This avoids poor performance in storage accounts with soft-delete or blob versioning enabled.\n // https://github.com/Azure/azure-sdk-for-js/issues/10132\n return await blockBlobClient.setMetadata(metadata as Metadata, {\n abortSignal,\n tracingOptions,\n });\n } catch (err: any) {\n // Check if the error is `BlobNotFound` and fallback to `upload` if it is.\n if (err?.name !== \"RestError\") {\n throw err;\n }\n const errorDetails = (err as RestError).details as { [field: string]: string } | undefined;\n const errorCode = errorDetails?.errorCode;\n if (!errorCode || errorCode !== \"BlobNotFound\") {\n throw err;\n }\n\n return blockBlobClient.upload(\"\", 0, {\n abortSignal,\n metadata: metadata as Metadata,\n tracingOptions,\n });\n }\n }\n }\n}\n\ntype OwnershipMetadata = {\n [k in \"ownerid\"]: string | undefined;\n};\n\ntype CheckpointMetadata = {\n [k in \"sequencenumber\" | \"offset\"]: string | undefined;\n};\n\n/**\n * @internal\n */\nexport function parseIntOrThrow(\n blobName: string,\n fieldName: string,\n numStr: string | undefined,\n): number {\n if (numStr == null) {\n throw new Error(`Missing metadata property '${fieldName}' on blob '${blobName}'`);\n }\n\n const num = parseInt(numStr, 10);\n\n if (isNaN(num)) {\n throw new Error(\n `Failed to parse metadata property '${fieldName}' on blob '${blobName}' as a number`,\n );\n }\n\n return num;\n}\n"]}
1
+ {"version":3,"file":"blobCheckpointStore.js","sourceRoot":"","sources":["../../src/blobCheckpointStore.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n CheckpointStore,\n PartitionOwnership,\n Checkpoint,\n OperationOptions,\n} from \"@azure/event-hubs\";\nimport type { Metadata, RestError, BlobSetMetadataResponse } from \"@azure/storage-blob\";\nimport { logger, logErrorStackTrace } from \"./log.js\";\nimport type { ContainerClientLike } from \"./storageBlobInterfaces.js\";\nimport { throwTypeErrorIfParameterMissing } from \"./util/error.js\";\n\n/**\n * An implementation of CheckpointStore that uses Azure Blob Storage to persist checkpoint data.\n */\nexport class BlobCheckpointStore implements CheckpointStore {\n private _containerClient: ContainerClientLike;\n\n /**\n * Constructs a new instance of {@link BlobCheckpointStore}\n * @param containerClient - An instance of a storage blob ContainerClient.\n */\n constructor(containerClient: ContainerClientLike) {\n this._containerClient = containerClient;\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 options: OperationOptions = {},\n ): Promise<PartitionOwnership[]> {\n const partitionOwnershipArray: PartitionOwnership[] = [];\n const { abortSignal, tracingOptions } = options;\n\n const blobPrefix = BlobCheckpointStore.getBlobPrefix({\n type: \"ownership\",\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup: consumerGroup,\n });\n\n try {\n const blobs = this._containerClient.listBlobsFlat({\n abortSignal,\n includeMetadata: true,\n prefix: blobPrefix,\n tracingOptions,\n });\n\n for await (const blob of blobs) {\n const blobPath = blob.name.split(\"/\");\n const blobName = blobPath[blobPath.length - 1];\n\n const ownershipMetadata = (blob.metadata as OwnershipMetadata) ?? {};\n\n if (ownershipMetadata.ownerid == null) {\n throw new Error(`Missing ownerid in metadata for blob ${blob.name}`);\n }\n\n const partitionOwnership: PartitionOwnership = {\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup: consumerGroup,\n ownerId: ownershipMetadata.ownerid,\n partitionId: blobName,\n lastModifiedTimeInMs:\n blob.properties.lastModified && blob.properties.lastModified.getTime(),\n etag: blob.properties.etag,\n };\n partitionOwnershipArray.push(partitionOwnership);\n }\n return partitionOwnershipArray;\n } catch (err: any) {\n logger.warning(`Error occurred while fetching the list of blobs`, err.message);\n logErrorStackTrace(err);\n\n if (err?.name === \"AbortError\") throw err;\n\n throw new Error(`Error occurred while fetching the list of blobs. \\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(\n partitionOwnership: PartitionOwnership[],\n options: OperationOptions = {},\n ): Promise<PartitionOwnership[]> {\n const partitionOwnershipArray: PartitionOwnership[] = [];\n for (const ownership of partitionOwnership) {\n const blobName = BlobCheckpointStore.getBlobPrefix({ type: \"ownership\", ...ownership });\n try {\n const updatedBlobResponse = await this._setBlobMetadata(\n blobName,\n {\n ownerid: ownership.ownerId,\n },\n ownership.etag,\n options,\n );\n\n if (updatedBlobResponse.lastModified) {\n ownership.lastModifiedTimeInMs = updatedBlobResponse.lastModified.getTime();\n }\n\n ownership.etag = updatedBlobResponse.etag;\n partitionOwnershipArray.push(ownership);\n logger.info(\n `[${ownership.ownerId}] Claimed ownership successfully for partition: ${ownership.partitionId}`,\n `LastModifiedTime: ${ownership.lastModifiedTimeInMs}, ETag: ${ownership.etag}`,\n );\n } catch (err: any) {\n const restError = err as RestError;\n\n if (restError.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\n logger.warning(\n `Error occurred while claiming ownership for partition: ${ownership.partitionId}`,\n err.message,\n );\n logErrorStackTrace(err);\n\n throw 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 options: OperationOptions = {},\n ): Promise<Checkpoint[]> {\n const { abortSignal, tracingOptions } = options;\n const blobPrefix = BlobCheckpointStore.getBlobPrefix({\n type: \"checkpoint\",\n fullyQualifiedNamespace,\n eventHubName,\n consumerGroup,\n });\n\n const blobs = this._containerClient.listBlobsFlat({\n abortSignal,\n includeMetadata: true,\n prefix: blobPrefix,\n tracingOptions,\n });\n\n const checkpoints: Checkpoint[] = [];\n\n for await (const blob of blobs) {\n const blobPath = blob.name.split(\"/\");\n const blobName = blobPath[blobPath.length - 1];\n\n const checkpointMetadata = (blob.metadata as CheckpointMetadata) ?? {};\n\n const offset = checkpointMetadata.offset;\n if (offset == null) {\n throw new Error(`Missing metadata property 'offset' on blob '${blob.name}'`);\n }\n const sequenceNumber = parseIntOrThrow(\n blob.name,\n \"sequencenumber\",\n checkpointMetadata.sequencenumber,\n );\n\n checkpoints.push({\n consumerGroup,\n eventHubName,\n fullyQualifiedNamespace,\n partitionId: blobName,\n offset,\n sequenceNumber,\n });\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 The new etag on successful update.\n */\n async updateCheckpoint(checkpoint: Checkpoint, options: OperationOptions = {}): Promise<void> {\n throwTypeErrorIfParameterMissing(\n \"updateCheckpoint\",\n \"sequenceNumber\",\n checkpoint.sequenceNumber,\n );\n throwTypeErrorIfParameterMissing(\"updateCheckpoint\", \"offset\", checkpoint.offset);\n\n const blobName = BlobCheckpointStore.getBlobPrefix({ type: \"checkpoint\", ...checkpoint });\n try {\n const metadataResponse = await this._setBlobMetadata(\n blobName,\n {\n sequencenumber: checkpoint.sequenceNumber.toString(),\n offset: checkpoint.offset.toString(),\n },\n undefined,\n options,\n );\n\n logger.verbose(\n `Updated checkpoint successfully for partition: ${checkpoint.partitionId}`,\n `LastModifiedTime: ${metadataResponse.lastModified!.toISOString()}, ETag: ${\n metadataResponse.etag\n }`,\n );\n return;\n } catch (err: any) {\n logger.warning(\n `Error occurred while updating the checkpoint for partition: ${checkpoint.partitionId}.`,\n err.message,\n );\n logErrorStackTrace(err);\n\n if (err?.name === \"AbortError\") throw err;\n\n throw err;\n }\n }\n\n private static getBlobPrefix(params: {\n type: \"ownership\" | \"checkpoint\";\n fullyQualifiedNamespace: string;\n eventHubName: string;\n consumerGroup: string;\n partitionId?: string;\n }): string {\n // none of these are case-sensitive in eventhubs so we need to make sure we don't accidentally allow\n // the user to create a case-sensitive blob for their state!\n const consumerGroupName = params.consumerGroup.toLowerCase();\n const eventHubName = params.eventHubName.toLowerCase();\n const fullyQualifiedNamespace = params.fullyQualifiedNamespace.toLowerCase();\n\n if (params.partitionId) {\n return `${fullyQualifiedNamespace}/${eventHubName}/${consumerGroupName}/${params.type}/${params.partitionId}`;\n } else {\n return `${fullyQualifiedNamespace}/${eventHubName}/${consumerGroupName}/${params.type}/`;\n }\n }\n\n private async _setBlobMetadata(\n blobName: string,\n metadata: OwnershipMetadata | CheckpointMetadata,\n etag: string | undefined,\n options: OperationOptions = {},\n ): Promise<BlobSetMetadataResponse> {\n const { abortSignal, tracingOptions } = options;\n const blockBlobClient = this._containerClient.getBlobClient(blobName).getBlockBlobClient();\n\n // When we have an etag, we know the blob existed.\n // If we encounter an error we should fail.\n if (etag) {\n return blockBlobClient.setMetadata(metadata as Metadata, {\n abortSignal,\n conditions: {\n ifMatch: etag,\n },\n tracingOptions,\n });\n } else {\n try {\n // Attempt to set metadata, and fallback to upload if the blob doesn't already exist.\n // This avoids poor performance in storage accounts with soft-delete or blob versioning enabled.\n // https://github.com/Azure/azure-sdk-for-js/issues/10132\n return await blockBlobClient.setMetadata(metadata as Metadata, {\n abortSignal,\n tracingOptions,\n });\n } catch (err: any) {\n // Check if the error is `BlobNotFound` and fallback to `upload` if it is.\n if (err?.name !== \"RestError\") {\n throw err;\n }\n const errorDetails = (err as RestError).details as { [field: string]: string } | undefined;\n const errorCode = errorDetails?.errorCode;\n if (!errorCode || errorCode !== \"BlobNotFound\") {\n throw err;\n }\n\n return blockBlobClient.upload(\"\", 0, {\n abortSignal,\n metadata: metadata as Metadata,\n tracingOptions,\n });\n }\n }\n }\n}\n\ntype OwnershipMetadata = {\n [k in \"ownerid\"]: string | undefined;\n};\n\ntype CheckpointMetadata = {\n [k in \"sequencenumber\" | \"offset\"]: string | undefined;\n};\n\n/**\n * @internal\n */\nexport function parseIntOrThrow(\n blobName: string,\n fieldName: string,\n numStr: string | undefined,\n): number {\n if (numStr == null) {\n throw new Error(`Missing metadata property '${fieldName}' on blob '${blobName}'`);\n }\n\n const num = parseInt(numStr, 10);\n\n if (isNaN(num)) {\n throw new Error(\n `Failed to parse metadata property '${fieldName}' on blob '${blobName}' as a number`,\n );\n }\n\n return num;\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@azure/eventhubs-checkpointstore-blob",
3
3
  "sdk-type": "client",
4
- "version": "2.0.1-alpha.20250619.1",
4
+ "version": "2.0.1-alpha.20250730.1",
5
5
  "description": "An Azure Storage Blob solution to store checkpoints when using Event Hubs.",
6
6
  "author": "Microsoft Corporation",
7
7
  "license": "MIT",