mirror of
https://github.com/SukkaW/Surge.git
synced 2026-04-30 01:46:57 +08:00
Refactor: Cross Realm Span
This commit is contained in:
@@ -147,7 +147,7 @@ export const DOMAIN_LISTS_EXTRA: HostsSource[] = [
|
|||||||
true
|
true
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'https://raw.githubusercontent.com/DandelionSprout/adfilt/refs/heads/master/Alternate%20versions%20Anti-Malware%20List/AntiMalwareDomains.txt',
|
'https://raw.githubusercontent.com/DandelionSprout/adfilt/master/Alternate%20versions%20Anti-Malware%20List/AntiMalwareDomains.txt',
|
||||||
[],
|
[],
|
||||||
true
|
true
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ import { downloadMockAssets } from './download-mock-assets';
|
|||||||
|
|
||||||
import { buildCloudMounterRules } from './build-cloudmounter-rules';
|
import { buildCloudMounterRules } from './build-cloudmounter-rules';
|
||||||
|
|
||||||
import { createSpan, printTraceResult, whyIsNodeRunning } from './trace';
|
import { printStats, printTraceResult, whyIsNodeRunning } from './trace';
|
||||||
|
import type { TraceResult } from './trace';
|
||||||
import { buildDeprecateFiles } from './build-deprecate-files';
|
import { buildDeprecateFiles } from './build-deprecate-files';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { ROOT_DIR } from './constants/dir';
|
import { ROOT_DIR } from './constants/dir';
|
||||||
@@ -66,8 +67,6 @@ const buildFinishedLock = path.join(ROOT_DIR, '.BUILD_FINISHED');
|
|||||||
|
|
||||||
console.log(`Memory: ${os.totalmem() / (1024 * 1024)} MiB`);
|
console.log(`Memory: ${os.totalmem() / (1024 * 1024)} MiB`);
|
||||||
|
|
||||||
const rootSpan = createSpan('root');
|
|
||||||
|
|
||||||
if (fs.existsSync(buildFinishedLock)) {
|
if (fs.existsSync(buildFinishedLock)) {
|
||||||
fs.unlinkSync(buildFinishedLock);
|
fs.unlinkSync(buildFinishedLock);
|
||||||
}
|
}
|
||||||
@@ -78,39 +77,62 @@ const buildFinishedLock = path.join(ROOT_DIR, '.BUILD_FINISHED');
|
|||||||
await import('why-is-node-running');
|
await import('why-is-node-running');
|
||||||
}
|
}
|
||||||
|
|
||||||
const downloadPreviousBuildPromise = downloadPreviousBuild(rootSpan);
|
const downloadPreviousBuildPromise = downloadPreviousBuild();
|
||||||
const buildCommonPromise = downloadPreviousBuildPromise.then(() => buildCommon(rootSpan));
|
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
downloadPreviousBuildPromise,
|
downloadPreviousBuildPromise,
|
||||||
buildCommonPromise,
|
downloadPreviousBuildPromise.then(() => buildCommon()),
|
||||||
downloadPreviousBuildPromise.then(() => buildRejectIPList(rootSpan)),
|
downloadPreviousBuildPromise.then(() => buildRejectIPList()),
|
||||||
downloadPreviousBuildPromise.then(() => buildAppleCdn(rootSpan)),
|
downloadPreviousBuildPromise.then(() => buildAppleCdn()),
|
||||||
downloadPreviousBuildPromise.then(() => buildCdnDownloadConf(rootSpan)),
|
downloadPreviousBuildPromise.then(() => buildCdnDownloadConf()),
|
||||||
downloadPreviousBuildPromise.then(() => buildRejectDomainSet(rootSpan)),
|
downloadPreviousBuildPromise.then(() => buildRejectDomainSet()),
|
||||||
downloadPreviousBuildPromise.then(() => buildTelegramCIDR(rootSpan)),
|
downloadPreviousBuildPromise.then(() => buildTelegramCIDR()),
|
||||||
downloadPreviousBuildPromise.then(() => buildChnCidr(rootSpan)),
|
downloadPreviousBuildPromise.then(() => buildChnCidr()),
|
||||||
downloadPreviousBuildPromise.then(() => buildSpeedtestDomainSet(rootSpan)),
|
downloadPreviousBuildPromise.then(() => buildSpeedtestDomainSet()),
|
||||||
downloadPreviousBuildPromise.then(() => buildDomesticRuleset(rootSpan)),
|
downloadPreviousBuildPromise.then(() => buildDomesticRuleset()),
|
||||||
downloadPreviousBuildPromise.then(() => buildGlobalRuleset(rootSpan)),
|
downloadPreviousBuildPromise.then(() => buildGlobalRuleset()),
|
||||||
downloadPreviousBuildPromise.then(() => buildRedirectModule(rootSpan)),
|
downloadPreviousBuildPromise.then(() => buildRedirectModule()),
|
||||||
downloadPreviousBuildPromise.then(() => buildAlwaysRealIPModule(rootSpan)),
|
downloadPreviousBuildPromise.then(() => buildAlwaysRealIPModule()),
|
||||||
downloadPreviousBuildPromise.then(() => buildStreamService(rootSpan)),
|
downloadPreviousBuildPromise.then(() => buildStreamService()),
|
||||||
downloadPreviousBuildPromise.then(() => buildMicrosoftCdn(rootSpan)),
|
downloadPreviousBuildPromise.then(() => buildMicrosoftCdn()),
|
||||||
downloadPreviousBuildPromise.then(() => buildCloudMounterRules(rootSpan)),
|
downloadPreviousBuildPromise.then(() => buildCloudMounterRules()),
|
||||||
downloadMockAssets(rootSpan)
|
downloadMockAssets()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await buildDeprecateFiles(rootSpan);
|
await buildDeprecateFiles();
|
||||||
await buildPublic(rootSpan);
|
await buildPublic();
|
||||||
|
|
||||||
rootSpan.stop();
|
|
||||||
|
|
||||||
printTraceResult(rootSpan.traceResult);
|
|
||||||
|
|
||||||
// write a file to demonstrate that the build is finished
|
// write a file to demonstrate that the build is finished
|
||||||
fs.writeFileSync(buildFinishedLock, 'BUILD_FINISHED\n');
|
fs.writeFileSync(buildFinishedLock, 'BUILD_FINISHED\n');
|
||||||
|
|
||||||
|
const traces: TraceResult[] = [];
|
||||||
|
[
|
||||||
|
downloadPreviousBuild,
|
||||||
|
downloadMockAssets,
|
||||||
|
buildCommon,
|
||||||
|
buildRejectIPList,
|
||||||
|
buildAppleCdn,
|
||||||
|
buildCdnDownloadConf,
|
||||||
|
buildRejectDomainSet,
|
||||||
|
buildTelegramCIDR,
|
||||||
|
buildChnCidr,
|
||||||
|
buildSpeedtestDomainSet,
|
||||||
|
buildDomesticRuleset,
|
||||||
|
buildGlobalRuleset,
|
||||||
|
buildRedirectModule,
|
||||||
|
buildAlwaysRealIPModule,
|
||||||
|
buildStreamService,
|
||||||
|
buildMicrosoftCdn,
|
||||||
|
buildCloudMounterRules,
|
||||||
|
buildPublic,
|
||||||
|
buildDeprecateFiles
|
||||||
|
].forEach((fn) => {
|
||||||
|
const trace = fn.getInternalTraceResult();
|
||||||
|
printTraceResult(trace);
|
||||||
|
traces.push(trace);
|
||||||
|
});
|
||||||
|
printStats(traces);
|
||||||
|
|
||||||
// Finish the build to avoid leaking timer/fetch ref
|
// Finish the build to avoid leaking timer/fetch ref
|
||||||
await whyIsNodeRunning();
|
await whyIsNodeRunning();
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
|||||||
@@ -28,11 +28,11 @@ export function processDomainListsWithPreload(
|
|||||||
const downloadPromise = fetchAssets(domainListsUrl, mirrors, true, allowEmptyRemote);
|
const downloadPromise = fetchAssets(domainListsUrl, mirrors, true, allowEmptyRemote);
|
||||||
const lineCb = includeAllSubDomain ? domainListLineCbIncludeAllSubdomain : domainListLineCb;
|
const lineCb = includeAllSubDomain ? domainListLineCbIncludeAllSubdomain : domainListLineCb;
|
||||||
|
|
||||||
return (span: Span) => span.traceChildAsync(`process domainlist: ${domainListsUrl}`, async (span) => {
|
return (span: Span) => span.traceChildAsync(`process domainlist: ${domainListsUrl}`, async (childSpan) => {
|
||||||
const filterRules = await span.traceChildPromise('download', downloadPromise);
|
const filterRules = await childSpan.traceChildPromise('download', downloadPromise);
|
||||||
const domainSets: string[] = [];
|
const domainSets: string[] = [];
|
||||||
|
|
||||||
span.traceChildSync('parse domain list', () => {
|
childSpan.traceChildSync('parse domain list', () => {
|
||||||
for (let i = 0, len = filterRules.length; i < len; i++) {
|
for (let i = 0, len = filterRules.length; i < len; i++) {
|
||||||
lineCb(filterRules[i], domainSets, domainListsUrl, fastNormalizeDomainWithoutWww);
|
lineCb(filterRules[i], domainSets, domainListsUrl, fastNormalizeDomainWithoutWww);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import { basename, extname } from 'node:path';
|
|||||||
import process from 'node:process';
|
import process from 'node:process';
|
||||||
import picocolors from 'picocolors';
|
import picocolors from 'picocolors';
|
||||||
|
|
||||||
const SPAN_STATUS_START = 0;
|
export const SPAN_STATUS_START = 0;
|
||||||
const SPAN_STATUS_END = 1;
|
export const SPAN_STATUS_END = 1;
|
||||||
|
|
||||||
const spanTag = Symbol('span');
|
const spanTag = Symbol('span');
|
||||||
|
|
||||||
@@ -16,12 +16,11 @@ export interface TraceResult {
|
|||||||
children: TraceResult[]
|
children: TraceResult[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const rootTraceResult: TraceResult = {
|
/** Pure data object — safe to transfer across Worker Thread boundaries. */
|
||||||
name: 'root',
|
export interface RawSpan {
|
||||||
start: 0,
|
traceResult: TraceResult,
|
||||||
end: 0,
|
status: typeof SPAN_STATUS_START | typeof SPAN_STATUS_END
|
||||||
children: []
|
}
|
||||||
};
|
|
||||||
|
|
||||||
export interface Span {
|
export interface Span {
|
||||||
[spanTag]: true,
|
[spanTag]: true,
|
||||||
@@ -36,37 +35,23 @@ export interface Span {
|
|||||||
readonly traceResult: TraceResult
|
readonly traceResult: TraceResult
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createSpan(name: string, parentTraceResult?: TraceResult): Span {
|
/**
|
||||||
const start = performance.now();
|
* Wraps a serializable {@link RawSpan} with all span methods.
|
||||||
|
* Use this on a worker thread after receiving a {@link RawSpan} (or {@link TraceResult})
|
||||||
let curTraceResult: TraceResult;
|
* transferred from another thread.
|
||||||
|
*/
|
||||||
if (parentTraceResult == null) {
|
export function makeSpan(rawSpan: RawSpan): Span {
|
||||||
curTraceResult = rootTraceResult;
|
const { traceResult } = rawSpan;
|
||||||
} else {
|
|
||||||
curTraceResult = {
|
|
||||||
name,
|
|
||||||
start,
|
|
||||||
end: 0,
|
|
||||||
children: []
|
|
||||||
};
|
|
||||||
parentTraceResult.children.push(curTraceResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
let status: typeof SPAN_STATUS_START | typeof SPAN_STATUS_END = SPAN_STATUS_START;
|
|
||||||
|
|
||||||
const stop = (time?: number) => {
|
const stop = (time?: number) => {
|
||||||
if (status === SPAN_STATUS_END) {
|
if (rawSpan.status === SPAN_STATUS_END) {
|
||||||
throw new Error(`span already stopped: ${name}`);
|
throw new Error(`span already stopped: ${traceResult.name}`);
|
||||||
}
|
}
|
||||||
const end = time ?? performance.now();
|
traceResult.end = time ?? performance.now();
|
||||||
|
rawSpan.status = SPAN_STATUS_END;
|
||||||
curTraceResult.end = end;
|
|
||||||
|
|
||||||
status = SPAN_STATUS_END;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const traceChild = (name: string) => createSpan(name, curTraceResult);
|
const traceChild = (name: string) => createSpan(name, traceResult);
|
||||||
|
|
||||||
const span: Span = {
|
const span: Span = {
|
||||||
[spanTag]: true,
|
[spanTag]: true,
|
||||||
@@ -82,7 +67,7 @@ export function createSpan(name: string, parentTraceResult?: TraceResult): Span
|
|||||||
span.stop();
|
span.stop();
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
traceResult: curTraceResult,
|
traceResult,
|
||||||
async tracePromise<T>(promise: Promise<T>): Promise<T> {
|
async tracePromise<T>(promise: Promise<T>): Promise<T> {
|
||||||
const res = await promise;
|
const res = await promise;
|
||||||
span.stop();
|
span.stop();
|
||||||
@@ -97,18 +82,35 @@ export function createSpan(name: string, parentTraceResult?: TraceResult): Span
|
|||||||
return span;
|
return span;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createSpan(name: string, parentTraceResult?: TraceResult): Span {
|
||||||
|
const rawSpan: RawSpan = {
|
||||||
|
traceResult: {
|
||||||
|
name,
|
||||||
|
start: performance.now(),
|
||||||
|
end: 0,
|
||||||
|
children: []
|
||||||
|
},
|
||||||
|
status: SPAN_STATUS_START
|
||||||
|
};
|
||||||
|
|
||||||
|
parentTraceResult?.children.push(rawSpan.traceResult);
|
||||||
|
|
||||||
|
return makeSpan(rawSpan);
|
||||||
|
}
|
||||||
|
|
||||||
export const dummySpan = createSpan('dummy');
|
export const dummySpan = createSpan('dummy');
|
||||||
|
|
||||||
export function task(importMetaMain: boolean, importMetaPath: string) {
|
export function task(importMetaMain: boolean, importMetaPath: string) {
|
||||||
return <T>(fn: (span: Span, onCleanup: (cb: () => Promise<void> | void) => void) => Promise<T>, customName?: string) => {
|
return (fn: (span: Span, onCleanup: (cb: () => Promise<void> | void) => void) => Promise<unknown>, customName?: string) => {
|
||||||
const taskName = customName ?? basename(importMetaPath, extname(importMetaPath));
|
const taskName = customName ?? basename(importMetaPath, extname(importMetaPath));
|
||||||
let cleanup: () => Promise<void> | void = noop;
|
let cleanup: () => Promise<void> | void = noop;
|
||||||
const onCleanup = (cb: () => void) => {
|
const onCleanup = (cb: () => void) => {
|
||||||
cleanup = cb;
|
cleanup = cb;
|
||||||
};
|
};
|
||||||
|
|
||||||
const innerSpan = createSpan(taskName);
|
|
||||||
if (importMetaMain) {
|
if (importMetaMain) {
|
||||||
|
const innerSpan = createSpan(taskName);
|
||||||
|
|
||||||
process.on('uncaughtException', (error) => {
|
process.on('uncaughtException', (error) => {
|
||||||
console.error('Uncaught exception:', error);
|
console.error('Uncaught exception:', error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@@ -126,15 +128,26 @@ export function task(importMetaMain: boolean, importMetaPath: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function run(span?: Span | null): Promise<T> {
|
let runSpan: Span;
|
||||||
return fn(span || innerSpan, onCleanup).finally(() => {
|
async function run(parentSpan?: Span | null): Promise<TraceResult> {
|
||||||
(span || innerSpan).stop();
|
if (parentSpan) {
|
||||||
|
runSpan = parentSpan.traceChild(taskName);
|
||||||
|
} else {
|
||||||
|
runSpan = createSpan(taskName);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fn(runSpan, onCleanup);
|
||||||
|
} finally {
|
||||||
|
runSpan.stop();
|
||||||
cleanup();
|
cleanup();
|
||||||
});
|
}
|
||||||
|
|
||||||
|
return runSpan.traceResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.assign(run, {
|
return Object.assign(run, {
|
||||||
getInternalTraceResult: () => innerSpan.traceResult
|
getInternalTraceResult: () => runSpan.traceResult
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -159,8 +172,7 @@ export async function whyIsNodeRunning() {
|
|||||||
// };
|
// };
|
||||||
// };
|
// };
|
||||||
|
|
||||||
export function printTraceResult(traceResult: TraceResult = rootTraceResult) {
|
export function printTraceResult(traceResult: TraceResult) {
|
||||||
printStats(traceResult.children);
|
|
||||||
printTree(
|
printTree(
|
||||||
traceResult,
|
traceResult,
|
||||||
node => {
|
node => {
|
||||||
@@ -206,7 +218,7 @@ function printTree(initialTree: TraceResult, printNode: (node: TraceResult, bran
|
|||||||
printBranch(initialTree, '', true, false);
|
printBranch(initialTree, '', true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function printStats(stats: TraceResult[]): void {
|
export function printStats(stats: TraceResult[]): void {
|
||||||
const longestTaskName = Math.max(...stats.map(i => i.name.length));
|
const longestTaskName = Math.max(...stats.map(i => i.name.length));
|
||||||
const realStart = Math.min(...stats.map(i => i.start));
|
const realStart = Math.min(...stats.map(i => i.start));
|
||||||
const realEnd = Math.max(...stats.map(i => i.end));
|
const realEnd = Math.max(...stats.map(i => i.end));
|
||||||
|
|||||||
Reference in New Issue
Block a user