UNPKG

reduct-js

Version:

ReductStore Client SDK for Javascript/NodeJS/Typescript

156 lines (155 loc) 4.97 kB
import { APIError } from "./APIError"; /** * Represents a batch of records for writing */ export var BatchType; (function (BatchType) { BatchType[BatchType["WRITE"] = 0] = "WRITE"; BatchType[BatchType["UPDATE"] = 1] = "UPDATE"; BatchType[BatchType["REMOVE"] = 2] = "REMOVE"; })(BatchType || (BatchType = {})); export class Batch { constructor(bucketName, entryName, httpClient, type) { this.bucketName = bucketName; this.entryName = entryName; this.httpClient = httpClient; this.records = new Map(); this.type = type; this.totalSize = BigInt(0); this.lastAccess = 0; } /** * Add record to batch * @param ts timestamp of record as a UNIX timestamp in microseconds * @param data {Buffer | string} data to write * @param contentType default: application/octet-stream * @param labels default: {} */ add(ts, data, contentType, labels) { const _contentType = contentType ?? "application/octet-stream"; const _labels = labels ?? {}; const _data = data instanceof Buffer ? data : Buffer.from(data, "utf-8"); this.totalSize += BigInt(_data.length); this.lastAccess = Date.now(); this.records.set(ts, { data: _data, contentType: _contentType, labels: _labels, }); } /** * Add only labels to batch * Use for updating labels * @param ts timestamp of record as a UNIX timestamp in microseconds * @param labels */ addOnlyLabels(ts, labels) { this.records.set(ts, { data: Buffer.from(""), contentType: "", labels: labels, }); } /** * Add only timestamp to batch * Use for removing records * @param ts timestamp of record as a UNIX timestamp in microseconds */ addOnlyTimestamp(ts) { this.records.set(ts, { data: Buffer.from(""), contentType: "", labels: {}, }); } /** * Write batch to entry */ async write() { const headers = {}; const chunks = []; let contentLength = 0; for (const [ts, { data, contentType, labels }] of this.items()) { contentLength += data.length; chunks.push(data); const headerName = `x-reduct-time-${ts}`; let headerValue = "0,"; if (this.type == BatchType.WRITE) { headerValue = `${data.length},${contentType}`; } for (const [key, value] of Object.entries(labels)) { if (value.toString().includes(",")) { headerValue += `,${key}="${value}"`; } else { headerValue += `,${key}=${value}`; } } headers[headerName] = headerValue; } let response; switch (this.type) { case BatchType.WRITE: { const stream = new ReadableStream({ start(ctrl) { for (const chunk of chunks) { ctrl.enqueue(chunk); } ctrl.close(); }, }); headers["Content-Type"] = "application/octet-stream"; headers["Content-Length"] = contentLength.toString(); response = await this.httpClient.post(`/b/${this.bucketName}/${this.entryName}/batch`, stream, headers); break; } case BatchType.UPDATE: response = await this.httpClient.patch(`/b/${this.bucketName}/${this.entryName}/batch`, "", headers); break; case BatchType.REMOVE: response = await this.httpClient.delete(`/b/${this.bucketName}/${this.entryName}/batch`, headers); break; } const errors = new Map(); for (const [key, value] of response.headers.entries()) { if (key.startsWith("x-reduct-error-")) { const ts = BigInt(key.slice(15)); const [code, message] = value.split(",", 2); errors.set(ts, new APIError(message, Number.parseInt(code))); } } return errors; } /** * Get records in batch sorted by timestamp */ items() { return new Map([...this.records.entries()].sort()).entries(); } /** * Get total size of batch */ size() { return this.totalSize; } /** * Get last access time of batch */ lastAccessTime() { return this.lastAccess; } /** * Get number of records in batch */ recordCount() { return this.records.size; } /** * Clear batch */ clear() { this.records.clear(); this.totalSize = BigInt(0); this.lastAccess = 0; } }