inngest
Version:
Official SDK for Inngest.com. Inngest is the reliability layer for modern applications. Inngest combines durable execution, events, and queues into a zero-infra platform with built-in observability.
92 lines (90 loc) • 3.23 kB
JavaScript
import { headerKeys } from "../../helpers/consts.js";
import { FlushResponse } from "../../proto/src/components/connect/protobuf/connect.js";
import { expBackoff } from "./util.js";
//#region src/components/connect/buffer.ts
var MessageBuffer = class {
buffered = {};
pending = {};
getApiBaseUrl;
logger;
envName;
constructor({ envName, getApiBaseUrl, logger }) {
this.envName = envName;
this.getApiBaseUrl = getApiBaseUrl;
this.logger = logger;
}
append(requestId, responseBytes) {
this.buffered[requestId] = responseBytes;
delete this.pending[requestId];
}
addPending(requestId, responseBytes, deadline) {
this.pending[requestId] = responseBytes;
setTimeout(() => {
if (this.pending[requestId]) {
this.logger.warn({ requestId }, "Message not acknowledged in time");
this.append(requestId, this.pending[requestId]);
}
}, deadline);
}
acknowledgePending(requestId) {
delete this.pending[requestId];
}
async sendFlushRequest(hashedSigningKey, responseBytes) {
const headers = {
"Content-Type": "application/protobuf",
...hashedSigningKey ? { Authorization: `Bearer ${hashedSigningKey}` } : {}
};
if (this.envName) headers[headerKeys.Environment] = this.envName;
if (!isUnsharedArrayBuffer(responseBytes)) throw new Error("Unreachable: response bytes are not an ArrayBuffer");
const resp = await fetch(new URL("/v0/connect/flush", await this.getApiBaseUrl()), {
method: "POST",
body: responseBytes,
headers
});
if (!resp.ok) {
this.logger.error({
body: await resp.text(),
status: resp.status
}, "Failed to flush messages");
throw new Error("Failed to flush messages");
}
return FlushResponse.decode(new Uint8Array(await resp.arrayBuffer()));
}
async flush(hashedSigningKey) {
if (Object.keys(this.buffered).length === 0) return;
this.logger.info({ count: Object.keys(this.buffered).length }, "Flushing messages");
const maxAttempts = 5;
for (let attempt = 0; attempt < maxAttempts; attempt++) {
for (const [requestId, v] of Object.entries(this.buffered)) try {
await this.sendFlushRequest(hashedSigningKey, v);
delete this.buffered[requestId];
} catch (err) {
this.logger.warn({
err,
requestId
}, "Failed to flush message");
break;
}
if (Object.keys(this.buffered).length === 0) return;
await new Promise((resolve) => setTimeout(resolve, expBackoff(attempt)));
}
this.logger.error({ maxAttempts }, "Failed to flush messages after max attempts");
}
};
function isUnsharedArrayBuffer(value) {
if (typeof SharedArrayBuffer === "undefined") return true;
return value.buffer instanceof ArrayBuffer;
}
/**
* Throws an error if the value is not an unshared ArrayBuffer. This should be
* safe because we shouldn't be using `SharedArrayBuffer` at runtime, but our
* protobuf types have `Uint8Array` as the return type (no generic), which
* effectively defaults to a union of `ArrayBuffer` and `SharedArrayBuffer`.
*/
function ensureUnsharedArrayBuffer(value) {
if (!isUnsharedArrayBuffer(value)) throw new Error("Unreachable: response bytes are not an ArrayBuffer");
return value;
}
//#endregion
export { MessageBuffer, ensureUnsharedArrayBuffer };
//# sourceMappingURL=buffer.js.map