@azure/cosmos
Version:
Microsoft Azure Cosmos DB Service Node.js SDK for NOSQL API
187 lines (186 loc) • 7.56 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var Batcher_exports = {};
__export(Batcher_exports, {
Batcher: () => Batcher
});
module.exports = __toCommonJS(Batcher_exports);
var import_constants = require("../common/constants.js");
var import_statusCodes = require("../common/statusCodes.js");
var import_DiagnosticNodeInternal = require("../diagnostics/DiagnosticNodeInternal.js");
var import__ = require("../index.js");
var import_batch = require("../utils/batch.js");
var import_time = require("../utils/time.js");
class Batcher {
batchOperationsList;
currentSize;
toBeDispatched;
executor;
retrier;
diagnosticLevel;
encryptionEnabled;
encryptionProcessor;
clientConfigDiagnostics;
limiter;
processedOperationCountRef;
constructor(limiter, executor, retrier, diagnosticLevel, encryptionEnabled, clientConfig, encryptionProcessor, processedOperationCountRef) {
this.limiter = limiter;
this.batchOperationsList = [];
this.executor = executor;
this.retrier = retrier;
this.diagnosticLevel = diagnosticLevel;
this.encryptionEnabled = encryptionEnabled;
this.encryptionProcessor = encryptionProcessor;
this.clientConfigDiagnostics = clientConfig;
this.currentSize = 0;
this.toBeDispatched = false;
this.processedOperationCountRef = processedOperationCountRef;
}
/**
* Attempts to add an operation to the current batch.
* Returns false if the batch is full or already dispatched.
*/
tryAdd(operation) {
if (this.toBeDispatched) {
return false;
}
if (!operation) {
throw new import__.ErrorResponse("Operation is not defined");
}
if (!operation.operationContext) {
throw new import__.ErrorResponse("Operation context is not defined");
}
if (this.batchOperationsList.length === import_constants.Constants.MaxBulkOperationsCount) {
return false;
}
const currentOperationSize = (0, import_batch.calculateObjectSizeInBytes)(operation.operationInput);
if (this.batchOperationsList.length > 0 && this.currentSize + currentOperationSize > import_constants.Constants.DefaultMaxBulkRequestBodySizeInBytes) {
return false;
}
this.currentSize += currentOperationSize;
this.batchOperationsList.push(operation);
return true;
}
isEmpty() {
return this.batchOperationsList.length === 0;
}
/**
* Dispatches the current batch of operations.
* Handles retries for failed operations and updates the ordered response.
*/
async dispatch(partitionMetric) {
this.toBeDispatched = true;
const startTime = (0, import_time.getCurrentTimestampInMs)();
const diagnosticNode = new import_DiagnosticNodeInternal.DiagnosticNodeInternal(
this.diagnosticLevel,
import_DiagnosticNodeInternal.DiagnosticNodeType.BATCH_REQUEST,
null
);
try {
const response = await this.executor(this.batchOperationsList, diagnosticNode);
const hasThrottles = 1;
const noThrottle = 0;
const numThrottle = response?.results?.some(
(result) => "code" in result && result.code === import_statusCodes.StatusCodes.TooManyRequests
) ? hasThrottles : noThrottle;
const splitOrMerge = response?.results?.some(
(result) => "code" in result && result.code === import_statusCodes.StatusCodes.Gone
) ? true : false;
if (splitOrMerge) {
await this.limiter.pauseAndClear(import_statusCodes.StatusCodes.Gone, diagnosticNode);
}
partitionMetric.add(
this.batchOperationsList.length,
(0, import_time.getCurrentTimestampInMs)() - startTime,
numThrottle
);
for (let i = 0; i < response.operations.length; i++) {
const operation = response.operations[i];
const bulkOperationResult = response.results[i];
if (bulkOperationResult instanceof import__.ErrorResponse) {
const shouldRetry = await operation.operationContext.retryPolicy.shouldRetry(
bulkOperationResult,
operation.operationContext.diagnosticNode
);
if (shouldRetry) {
await this.retrier(operation, operation.operationContext.diagnosticNode);
continue;
}
}
try {
if (this.encryptionEnabled && bulkOperationResult.resourceBody) {
operation.operationContext.diagnosticNode.beginEncryptionDiagnostics(
import_constants.Constants.Encryption.DiagnosticsDecryptOperation
);
const { body: decryptedBody, propertiesDecryptedCount } = await this.encryptionProcessor.decrypt(bulkOperationResult.resourceBody);
bulkOperationResult.resourceBody = decryptedBody;
operation.operationContext.diagnosticNode.endEncryptionDiagnostics(
import_constants.Constants.Encryption.DiagnosticsDecryptOperation,
propertiesDecryptedCount
);
}
} catch (error) {
if (operation.operationInput.operationType !== "Read") {
const decryptionError = new import__.ErrorResponse(
`Item ${operation.operationInput.operationType} operation was successful but response decryption failed: + ${error.message}`
);
decryptionError.code = import_statusCodes.StatusCodes.ServiceUnavailable;
throw decryptionError;
}
}
operation.operationContext.addDiagnosticChild(diagnosticNode);
bulkOperationResult.diagnostics = operation.operationContext.diagnosticNode.toDiagnostic(
this.clientConfigDiagnostics
);
const bulkItemResponse = {
operationInput: operation.unencryptedOperationInput
};
if (bulkOperationResult instanceof import__.ErrorResponse) {
bulkItemResponse.error = bulkOperationResult;
} else {
bulkItemResponse.response = bulkOperationResult;
}
operation.operationContext.complete(bulkItemResponse);
this.processedOperationCountRef.count++;
}
} catch (error) {
for (const operation of this.batchOperationsList) {
const response = {
operationInput: operation.operationInput,
error: Object.assign(new import__.ErrorResponse(error.message), {
code: import_statusCodes.StatusCodes.InternalServerError,
diagnostics: operation.operationContext.diagnosticNode.toDiagnostic(
this.clientConfigDiagnostics
)
})
};
operation.operationContext.fail(response);
this.processedOperationCountRef.count++;
}
} finally {
this.batchOperationsList = [];
}
}
getOperations() {
return this.batchOperationsList;
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Batcher
});