优化数据消耗

This commit is contained in:
imengying committed 2026-09-09 00:11:03 +08:00
1 parent a7194dd287
commit c971049290
35 files changed
+1491 -854

No files matched your search

+48 -9
View File
@@ -9,6 +9,7 @@ import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { setTimeout as delay } from "node:timers/promises";
import { WebSocketServer } from "ws";
import { gunzipSync } from "node:zlib";
const binary = resolve(process.env.MONITOR_AGENT_BINARY ?? "agent/target/debug/nodeflare-agent");
const directory = mkdtempSync(join(tmpdir(), "nodeflare-agent-runtime-"));
@@ -67,7 +68,7 @@ try {
proxy.listen(0, "127.0.0.1");
await once(proxy, "listening");
console.log("agent runtime: automatic update during one-second sampling");
console.log("agent runtime: automatic update during three-second sampling");
websocketServer = new WebSocketServer({ noServer: true });
upgradeServer = createHttpServer();
let redirected = false;
@@ -89,27 +90,30 @@ try {
socket.send(JSON.stringify({
type: "config", ts: Math.floor(Date.now() / 1_000),
config: {
report_interval: 15, collect_interval: 1, network_interface: "",
report_interval: 15, collect_interval: 3, network_interface: "",
agent_mirror: "", auto_update: true, latency_tasks: [],
},
}));
socket.on("message", (raw) => {
const message = JSON.parse(raw.toString());
if (message.type !== "update") return;
let receivedThrough = 0;
socket.on("message", (raw, binary) => {
if (!binary) return;
const message = JSON.parse(gunzipSync(raw).toString());
samples += message.samples.length;
if (message.persist) persistedThrough = Math.max(...message.samples.map((sample) => sample.timestamp));
receivedThrough = Math.max(receivedThrough, ...message.samples.map((sample) => sample.metrics.timestamp));
if (!message.persist) return;
persistedThrough = receivedThrough;
socket.send(JSON.stringify({
type: "ack", ts: Math.floor(Date.now() / 1_000),
persisted: message.persist, persistenceError: false,
persistedThroughTs: persistedThrough,
nextPersistAfterMs: 1_000, nextWssReportAfterMs: 1_000, realtimeHint: false,
nextPersistAfterMs: 15_000,
}));
});
});
let token;
for (let index = 0; index < 100_000; index++) {
const candidate = `runtime-update-${index}`;
if (createHash("sha256").update(candidate).digest().readBigUInt64BE(0) % 1_801n === 2n) {
if (createHash("sha256").update(candidate).digest().readBigUInt64BE(0) % 1_801n === 5n) {
token = candidate;
break;
}
@@ -121,6 +125,40 @@ try {
assert(redirected, "Same-origin WebSocket redirects must remain supported");
await stopAgent(updating);
console.log("agent runtime: three-second uploads are unique and reconnect replays unconfirmed samples");
const telemetryConnections = [];
const trackTelemetry = (socket) => {
const connection = { timestamps: [], durable: 0, firstInfo: false, infoCount: 0, commits: 0 };
telemetryConnections.push(connection);
socket.on("message", (raw, binary) => {
if (!binary) return;
const message = JSON.parse(gunzipSync(raw).toString());
for (const sample of message.samples) {
if (!connection.timestamps.length) connection.firstInfo = Boolean(sample.info);
connection.infoCount += Number(Boolean(sample.info));
assert(!connection.timestamps.includes(sample.metrics.timestamp), "Sample was uploaded twice on the same connection");
connection.timestamps.push(sample.metrics.timestamp);
}
if (message.persist) {
connection.durable = connection.timestamps.at(-1);
connection.commits++;
}
if (telemetryConnections.length === 1 && connection.timestamps.length === 3) socket.terminate();
});
};
websocketServer.on("connection", trackTelemetry);
const telemetry = startAgent(upgradeServer.address().port, "runtime-telemetry", "telemetry");
await waitUntil(() => telemetryConnections.length >= 2 && telemetryConnections[1].commits >= 2,
40_000, "Telemetry reconnect or commit did not complete");
await stopAgent(telemetry);
websocketServer.off("connection", trackTelemetry);
const [before, after] = telemetryConnections;
assert(before.firstInfo && after.firstInfo, "Static info must be resent on reconnect");
assert.equal(before.infoCount, 1, "Unchanged static info must not be uploaded repeatedly");
assert(before.timestamps.slice(1).every((timestamp) => after.timestamps.includes(timestamp)), "Unconfirmed samples were lost");
assert(!after.timestamps.includes(before.durable), "Durable samples must not be replayed");
assert(before.timestamps.slice(1).every((timestamp, index) => timestamp - before.timestamps[index] >= 3), "Sampling interval is shorter than three seconds");
console.log("agent runtime: remote command survives disconnect and returns its result");
const taskId = randomUUID();
let remoteConnections = 0;
@@ -128,7 +166,8 @@ try {
let remoteResult;
websocketServer.on("connection", (socket) => {
remoteConnections++;
socket.on("message", (raw) => {
socket.on("message", (raw, binary) => {
if (binary) return;
const message = JSON.parse(raw.toString());
if (message.type === "task_received" && message.task_id === taskId) {
received = true;
+1 -1
View File
@@ -272,7 +272,7 @@ MONITOR_BASE_URL="$MONITOR_BASE_URL" MONITOR_ADMIN_TOKEN="$admin_token" \
if [ -n "${MONITOR_AGENT_BINARY:-}" ]; then
step "real Agent failed-latency and persistence"
request -H "Authorization: Bearer $admin_token" -H 'Content-Type: application/json' -X PATCH \
--data "$(printf '%s' "$server_input" | jq '.report_interval=15 | .collect_interval=1')" \
--data "$(printf '%s' "$server_input" | jq '.report_interval=15 | .collect_interval=3')" \
"$MONITOR_BASE_URL/api/admin/servers/$server_id" >/dev/null
agent_latency_task_id=$(request -H "Authorization: Bearer $admin_token" \
-H 'Content-Type: application/json' \
+24 -40
View File
@@ -1,6 +1,22 @@
import WebSocket from "ws";
import assert from "node:assert/strict";
import { once } from "node:events";
import { gzipSync, gunzipSync } from "node:zlib";
function telemetryPayload(reports, persist) {
return gzipSync(JSON.stringify({
samples: reports.map((report) => {
const metrics = { ...report };
const info = {};
for (const key of ["cpu_model", "os", "kernel", "arch", "virtualization", "gpu_model", "agent_version", "ip_v4", "ip_v6"]) {
info[key] = metrics[key] ?? "";
delete metrics[key];
}
return { metrics, info };
}),
persist,
}));
}
const baseUrl = process.env.MONITOR_BASE_URL;
const adminToken = process.env.MONITOR_ADMIN_TOKEN;
@@ -190,10 +206,10 @@ function waitForJsonMessage(socket, expected, predicate) {
cleanup();
reject(error);
};
const onMessage = (data) => {
const onMessage = (data, binary) => {
let message;
try {
message = JSON.parse(data.toString());
message = JSON.parse((binary ? gunzipSync(data) : data).toString());
} catch {
return;
}
@@ -358,7 +374,7 @@ try {
name: "Offline remote smoke", region: "", group_name: "", tags: "", hidden: true,
expires_at: null, traffic_limit: 0, traffic_limit_type: "max", price: 0,
billing_cycle: 30, currency: "USD", auto_renewal: false, network_interface: "",
reset_day: 1, report_interval: 60, collect_interval: 1, rx_correction: 0,
reset_day: 1, report_interval: 60, collect_interval: 3, rx_correction: 0,
tx_correction: 0, agent_mirror: "", offline_notify_disabled: true, auto_update: false,
}),
});
@@ -554,27 +570,6 @@ try {
});
}
const wakeHintPromise = waitForJsonMessage(
agent,
"batched overview wake hint",
(message) => message.type === "ack" && message.realtimeHint === true,
);
const wakeResponse = await fetch(new URL("/api/live/wake", baseUrl), {
method: "POST",
headers: {
Authorization: `Bearer ${adminToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ server_ids: [serverId] }),
});
if (wakeResponse.status !== 204) {
throw new Error(`Batch wake returned HTTP ${wakeResponse.status}: ${await wakeResponse.text()}`);
}
const wakeHint = await wakeHintPromise;
if (wakeHint.nextWssReportAfterMs !== 5_000) {
throw new Error(`Invalid batch wake hint: ${JSON.stringify(wakeHint)}`);
}
const timestamp = Math.floor(Date.now() / 1_000);
const baseSample = {
timestamp,
@@ -654,22 +649,10 @@ try {
sample(5, { net_rx_total: 536870912, net_tx_total: 268435456 }),
];
const latestTimestamp = timestamp + 5;
for (const persist of [undefined, null]) {
const rejectedAck = waitForJsonMessage(
agent,
"invalid persistence flag ACK",
(message) => message.type === "ack" && message.realtimeHint === false,
);
agent.send(JSON.stringify({ type: "update", batchId: "invalid-persist", samples, persist }));
const rejected = await rejectedAck;
if (rejected.persistenceError !== true || rejected.persisted !== false) {
throw new Error(`Accepted invalid persistence flag: ${JSON.stringify(rejected)}`);
}
}
const ackPromise = waitForJsonMessage(
agent,
"Agent metric ACK",
(message) => message.type === "ack" && message.realtimeHint === false,
(message) => message.type === "ack",
);
const updatePromise = waitForJsonMessage(
dashboard,
@@ -684,7 +667,7 @@ try {
),
),
);
agent.send(JSON.stringify({ type: "update", batchId: `smoke-${timestamp}`, samples, persist: true }));
agent.send(telemetryPayload(samples, true));
const [ack, update] = await Promise.all([ackPromise, updatePromise]);
if (
ack.ts <= 0 ||
@@ -692,11 +675,12 @@ try {
ack.persistenceError !== false ||
!Number.isInteger(ack.persistedThroughTs) ||
ack.persistedThroughTs < latestTimestamp ||
ack.nextPersistAfterMs !== 60_000 ||
ack.nextWssReportAfterMs !== 5_000
ack.nextPersistAfterMs !== 60_000
) {
throw new Error(`Invalid Agent metric ACK: ${JSON.stringify(ack)}`);
}
assert.equal(update.updates[0].samples.length, 1, "Browser receives only the latest metric snapshot");
assert.equal(update.updates[0].samples[0].data.latency_results.length, 2, "All new latency results are retained");
const publicIdentityFields = [
"cpu_model",