This commit is contained in:
Nawaz Dhandala
2026-03-01 12:48:17 +00:00
parent 098a4e624f
commit 991a8a6f97
8 changed files with 73 additions and 75 deletions

View File

@@ -340,9 +340,7 @@ export class Service extends DatabaseService<Model> {
* Retrieves the stored WebAuthn challenge for the given user,
* validates it has not expired, and clears it so it cannot be reused.
*/
private async getAndClearStoredChallenge(
userId: ObjectID,
): Promise<string> {
private async getAndClearStoredChallenge(userId: ObjectID): Promise<string> {
const user: User | null = await UserService.findOneById({
id: userId,
select: {
@@ -375,8 +373,8 @@ export class Service extends DatabaseService<Model> {
await UserService.updateOneById({
id: userId,
data: {
webauthnChallenge: (null as any),
webauthnChallengeExpiresAt: (null as any),
webauthnChallenge: null as any,
webauthnChallengeExpiresAt: null as any,
},
props: {
isRoot: true,
@@ -394,8 +392,8 @@ export class Service extends DatabaseService<Model> {
await UserService.updateOneById({
id: userId,
data: {
webauthnChallenge: (null as any),
webauthnChallengeExpiresAt: (null as any),
webauthnChallenge: null as any,
webauthnChallengeExpiresAt: null as any,
},
props: {
isRoot: true,

View File

@@ -7,10 +7,7 @@ import https from "https";
import ivm from "isolated-vm";
import CaptureSpan from "../Telemetry/CaptureSpan";
export default class VMRunner {
@CaptureSpan()
public static async runCodeInSandbox(data: {
code: string;

View File

@@ -83,10 +83,7 @@ async function buildRssFeed(data: {
: `${data.baseUrlString}${post.blogUrl.startsWith("/") ? post.blogUrl : `/${post.blogUrl}`}`;
item.ele("link").txt(postUrl);
item
.ele("guid")
.att("isPermaLink", "true")
.txt(postUrl);
item.ele("guid").att("isPermaLink", "true").txt(postUrl);
item
.ele("pubDate")
.txt(new Date(`${post.postDate}T00:00:00.000Z`).toUTCString());
@@ -115,8 +112,7 @@ export async function generateBlogRssFeed(): Promise<string> {
const baseUrl: URL = await BlogPostUtil.getHomeUrl();
const baseUrlString: string = baseUrl.toString().replace(/\/$/, "");
const blogPosts: Array<BlogPostHeader> =
await BlogPostUtil.getBlogPostList();
const blogPosts: Array<BlogPostHeader> = await BlogPostUtil.getBlogPostList();
const xml: string = await buildRssFeed({
title: "OneUptime Blog",

View File

@@ -41,21 +41,19 @@ if (!process.env["PROBE_KEY"]) {
export const PROBE_KEY: string = process.env["PROBE_KEY"];
export const PROBE_MONITORING_WORKERS: number = NumberUtil.parseNumberWithDefault(
{
export const PROBE_MONITORING_WORKERS: number =
NumberUtil.parseNumberWithDefault({
value: process.env["PROBE_MONITORING_WORKERS"],
defaultValue: 1,
min: 1,
},
);
});
export const PROBE_MONITOR_FETCH_LIMIT: number = NumberUtil.parseNumberWithDefault(
{
export const PROBE_MONITOR_FETCH_LIMIT: number =
NumberUtil.parseNumberWithDefault({
value: process.env["PROBE_MONITOR_FETCH_LIMIT"],
defaultValue: 10,
min: 1,
},
);
});
export const HOSTNAME: string = process.env["HOSTNAME"] || "localhost";

View File

@@ -190,7 +190,10 @@ export default class Register {
options: { ...ProxyConfig.getRequestProxyAgents(aliveUrl) },
});
if (aliveResult instanceof HTTPErrorResponse || !aliveResult.isSuccess()) {
if (
aliveResult instanceof HTTPErrorResponse ||
!aliveResult.isSuccess()
) {
const errorMessage: string =
aliveResult instanceof HTTPErrorResponse
? aliveResult.message || JSON.stringify(aliveResult.data || {})

View File

@@ -70,11 +70,13 @@ export default class SyntheticMonitor {
}
}
// If we attempted executions but got zero results (all were skipped due to
// infrastructure errors like worker timeouts, OOM kills, or semaphore
// issues), return null to skip this entire check cycle. This prevents the
// monitor from flapping to the default status when the probe infrastructure
// is under load but the monitored service may be perfectly healthy.
/*
* If we attempted executions but got zero results (all were skipped due to
* infrastructure errors like worker timeouts, OOM kills, or semaphore
* issues), return null to skip this entire check cycle. This prevents the
* monitor from flapping to the default status when the probe infrastructure
* is under load but the monitored service may be perfectly healthy.
*/
if (totalExecutions > 0 && results.length === 0) {
logger.warn(
`Synthetic Monitor ${options?.monitorId?.toString()}: all ${totalExecutions} executions were skipped due to infrastructure issues, skipping this check cycle`,
@@ -103,9 +105,11 @@ export default class SyntheticMonitor {
try {
acquired = await SyntheticMonitorSemaphore.acquire(monitorIdStr);
} catch (err: unknown) {
// Semaphore errors (queue full, timeout waiting for slot) are infrastructure
// issues, not script failures. Skip this check cycle so the monitor stays in
// its last known state instead of flapping to offline.
/*
* Semaphore errors (queue full, timeout waiting for slot) are infrastructure
* issues, not script failures. Skip this check cycle so the monitor stays in
* its last known state instead of flapping to offline.
*/
logger.error(
`Synthetic monitor semaphore acquire failed (skipping this cycle): ${(err as Error)?.message}`,
);
@@ -243,11 +247,13 @@ export default class SyntheticMonitor {
scriptResult.screenshots = workerResult.screenshots;
scriptResult.executionTimeInMS = workerResult.executionTimeInMS;
} catch (err: unknown) {
// Errors thrown by the worker pool are always infrastructure issues (worker
// timeout, OOM kill, process crash, IPC failure) — NOT script failures.
// Actual script errors are returned inside WorkerResult.scriptError without
// throwing. Skip this check cycle so the monitor stays in its last known
// state instead of flapping between online and offline.
/*
* Errors thrown by the worker pool are always infrastructure issues (worker
* timeout, OOM kill, process crash, IPC failure) — NOT script failures.
* Actual script errors are returned inside WorkerResult.scriptError without
* throwing. Skip this check cycle so the monitor stays in its last known
* state instead of flapping between online and offline.
*/
logger.error(
`Synthetic monitor infrastructure error (skipping this cycle): ${(err as Error)?.message || String(err)}`,
);

View File

@@ -452,8 +452,10 @@ async function runExecution(config: WorkerConfig): Promise<WorkerResult> {
context = null;
}
// In --single-process mode, closing a context can destabilize the browser.
// Proactively check health so the next execution doesn't waste time on a zombie.
/*
* In --single-process mode, closing a context can destabilize the browser.
* Proactively check health so the next execution doesn't waste time on a zombie.
*/
if (currentBrowser && !currentBrowser.isConnected()) {
currentBrowser = null;
currentBrowserType = null;
@@ -530,9 +532,7 @@ async function shutdownGracefully(): Promise<void> {
process.exit(0);
}
function sendMessage(
msg: ReadyMessage | ResultMessage | ErrorMessage,
): void {
function sendMessage(msg: ReadyMessage | ResultMessage | ErrorMessage): void {
try {
if (process.send) {
process.send(msg);
@@ -598,25 +598,27 @@ function handleLegacyMessage(config: WorkerConfig): void {
}
})();
cleanup.then(() => {
if (process.send) {
const fallbackTimer: ReturnType<typeof setTimeout> = setTimeout(
() => {
process.exit(0);
},
IPC_FLUSH_TIMEOUT_MS,
);
cleanup
.then(() => {
if (process.send) {
const fallbackTimer: ReturnType<typeof setTimeout> = setTimeout(
() => {
process.exit(0);
},
IPC_FLUSH_TIMEOUT_MS,
);
process.send(result, () => {
clearTimeout(fallbackTimer);
process.send(result, () => {
clearTimeout(fallbackTimer);
process.exit(0);
});
} else {
process.exit(0);
});
} else {
process.exit(0);
}
}).catch(() => {
process.exit(1);
});
}
})
.catch(() => {
process.exit(1);
});
})
.catch((err: unknown) => {
clearTimeout(globalSafetyTimer);
@@ -653,8 +655,10 @@ process.on(
if (msg.type === "execute") {
const executeMsg: ExecuteMessage = msg as ExecuteMessage;
// Per-execution safety timer: if runExecution hangs (browser stuck, VM stuck),
// send an error back before the pool's timeout SIGKILL-s us with no message.
/*
* Per-execution safety timer: if runExecution hangs (browser stuck, VM stuck),
* send an error back before the pool's timeout SIGKILL-s us with no message.
*/
const safetyMarginMs: number = 15000;
const executionSafetyTimer: ReturnType<typeof setTimeout> = setTimeout(
() => {
@@ -665,9 +669,11 @@ process.on(
"Synthetic monitor worker safety timeout reached. " +
"The script or browser cleanup took too long.",
});
// Exit so the pool doesn't reuse this worker while runExecution
// is still in progress (would cause two concurrent executions
// sharing the same browser).
/*
* Exit so the pool doesn't reuse this worker while runExecution
* is still in progress (would cause two concurrent executions
* sharing the same browser).
*/
setTimeout(() => {
process.exit(1);
}, 5000); // give IPC 5s to flush the error message

View File

@@ -337,8 +337,7 @@ class SyntheticMonitorWorkerPool {
}
private rejectWorkerExecution(worker: PoolWorker, error: Error): void {
const reject: ((reason: Error) => void) | undefined =
worker.pendingReject;
const reject: ((reason: Error) => void) | undefined = worker.pendingReject;
this.clearWorkerPending(worker);
worker.busy = false;
@@ -373,8 +372,7 @@ class SyntheticMonitorWorkerPool {
`SyntheticMonitorWorkerPool: worker execution timed out, killing worker`,
);
const reject: ((reason: Error) => void) | undefined =
worker.pendingReject;
const reject: ((reason: Error) => void) | undefined = worker.pendingReject;
this.clearWorkerPending(worker);
this.removeWorker(worker);
@@ -430,9 +428,7 @@ class SyntheticMonitorWorkerPool {
}
private handleWorkerError(worker: PoolWorker, err: Error): void {
logger.error(
`SyntheticMonitorWorkerPool: worker error: ${err.message}`,
);
logger.error(`SyntheticMonitorWorkerPool: worker error: ${err.message}`);
if (worker.pendingReject) {
const reject: (reason: Error) => void = worker.pendingReject;
@@ -466,9 +462,7 @@ class SyntheticMonitorWorkerPool {
private retireWorker(worker: PoolWorker): void {
this.removeWorker(worker);
this.shutdownWorker(worker).catch((err: unknown) => {
logger.error(
`SyntheticMonitorWorkerPool: error retiring worker: ${err}`,
);
logger.error(`SyntheticMonitorWorkerPool: error retiring worker: ${err}`);
});
}