Finish fs cache changes / tracer optimization

This commit is contained in:
SukkaW 2024-02-02 11:21:16 +08:00
parent 8428b3da42
commit efa1ab254e
14 changed files with 77 additions and 65 deletions

View File

@ -37,7 +37,7 @@ export const buildAntiBogusDomain = task(import.meta.path, async (span) => {
const peeked = Bun.peek(getBogusNxDomainIPsPromise); const peeked = Bun.peek(getBogusNxDomainIPsPromise);
const bogusNxDomainIPs = peeked === getBogusNxDomainIPsPromise const bogusNxDomainIPs = peeked === getBogusNxDomainIPsPromise
? await span.traceChild('get bogus nxdomain ips').traceAsyncFn(() => getBogusNxDomainIPsPromise) ? await span.traceChildPromise('get bogus nxdomain ips', getBogusNxDomainIPsPromise)
: (peeked as string[]); : (peeked as string[]);
result.push(...bogusNxDomainIPs); result.push(...bogusNxDomainIPs);

View File

@ -21,7 +21,7 @@ export const buildAppleCdn = task(import.meta.path, async (span) => {
const promise = getAppleCdnDomainsPromise(); const promise = getAppleCdnDomainsPromise();
const peeked = Bun.peek(promise); const peeked = Bun.peek(promise);
const res: string[] = peeked === promise const res: string[] = peeked === promise
? await span.traceChild('get apple cdn domains').traceAsyncFn(() => promise) ? await span.traceChildPromise('get apple cdn domains', promise)
: (peeked as string[]); : (peeked as string[]);
const description = [ const description = [

View File

@ -32,7 +32,7 @@ export const buildChnCidr = task(import.meta.path, async (span) => {
const cidrPromise = getChnCidrPromise(); const cidrPromise = getChnCidrPromise();
const peeked = Bun.peek(cidrPromise); const peeked = Bun.peek(cidrPromise);
const filteredCidr: string[] = peeked === cidrPromise const filteredCidr: string[] = peeked === cidrPromise
? await span.traceChild('download chnroutes2').tracePromise(cidrPromise) ? await span.traceChildPromise('download chnroutes2', cidrPromise)
: (peeked as string[]); : (peeked as string[]);
// Can not use SHARED_DESCRIPTION here as different license // Can not use SHARED_DESCRIPTION here as different license

View File

@ -55,7 +55,7 @@ if (import.meta.main) {
const processFile = (span: Span, sourcePath: string) => { const processFile = (span: Span, sourcePath: string) => {
// console.log('Processing', sourcePath); // console.log('Processing', sourcePath);
return span.traceChild(`process file: ${sourcePath}`).traceAsyncFn(async () => { return span.traceChildAsync(`process file: ${sourcePath}`, async () => {
const lines: string[] = []; const lines: string[] = [];
let title = ''; let title = '';
@ -93,34 +93,36 @@ const processFile = (span: Span, sourcePath: string) => {
function transformDomainset(parentSpan: Span, sourcePath: string, relativePath: string) { function transformDomainset(parentSpan: Span, sourcePath: string, relativePath: string) {
return parentSpan return parentSpan
.traceChild(`transform domainset: ${path.basename(sourcePath, path.extname(sourcePath))}`) .traceChildAsync(
.traceAsyncFn(async (span) => { `transform domainset: ${path.basename(sourcePath, path.extname(sourcePath))}`,
const res = await processFile(span, sourcePath); async (span) => {
if (!res) return; const res = await processFile(span, sourcePath);
if (!res) return;
const [title, descriptions, lines] = res; const [title, descriptions, lines] = res;
const deduped = domainDeduper(lines); const deduped = domainDeduper(lines);
const description = [ const description = [
...SHARED_DESCRIPTION, ...SHARED_DESCRIPTION,
...( ...(
descriptions.length descriptions.length
? ['', ...descriptions] ? ['', ...descriptions]
: [] : []
) )
]; ];
return createRuleset( return createRuleset(
span, span,
title, title,
description, description,
new Date(), new Date(),
deduped, deduped,
'domainset', 'domainset',
path.resolve(outputSurgeDir, relativePath), path.resolve(outputSurgeDir, relativePath),
path.resolve(outputClashDir, `${relativePath.slice(0, -path.extname(relativePath).length)}.txt`) path.resolve(outputClashDir, `${relativePath.slice(0, -path.extname(relativePath).length)}.txt`)
); );
}); }
);
} }
/** /**

View File

@ -56,7 +56,7 @@ export const buildMicrosoftCdn = task(import.meta.path, async (span) => {
const promise = getMicrosoftCdnRulesetPromise(); const promise = getMicrosoftCdnRulesetPromise();
const peeked = Bun.peek(promise); const peeked = Bun.peek(promise);
const res: string[] = peeked === promise const res: string[] = peeked === promise
? await span.traceChild('get microsoft cdn domains').tracePromise(promise) ? await span.traceChildPromise('get microsoft cdn domains', promise)
: (peeked as string[]); : (peeked as string[]);
return createRuleset( return createRuleset(

View File

@ -68,7 +68,7 @@ export const buildRejectDomainSet = task(import.meta.path, async (span) => {
SetHelpers.add(domainSets, fullPhishingDomainSet); SetHelpers.add(domainSets, fullPhishingDomainSet);
setAddFromArray(domainSets, purePhishingDomains); setAddFromArray(domainSets, purePhishingDomains);
}), }),
childSpan.traceChild('process reject_sukka.conf').traceAsyncFn(async () => { childSpan.traceChildAsync('process reject_sukka.conf', async () => {
setAddFromArray(domainSets, await readFileIntoProcessedArray(path.resolve(import.meta.dir, '../Source/domainset/reject_sukka.conf'))); setAddFromArray(domainSets, await readFileIntoProcessedArray(path.resolve(import.meta.dir, '../Source/domainset/reject_sukka.conf')));
}) })
]); ]);
@ -84,13 +84,13 @@ export const buildRejectDomainSet = task(import.meta.path, async (span) => {
console.log(`Import ${previousSize} rules from Hosts / AdBlock Filter Rules & reject_sukka.conf!`); console.log(`Import ${previousSize} rules from Hosts / AdBlock Filter Rules & reject_sukka.conf!`);
// Dedupe domainSets // Dedupe domainSets
await span.traceChild('dedupe from black keywords/suffixes').traceAsyncFn(async (childSpan) => { await span.traceChildAsync('dedupe from black keywords/suffixes', async (childSpan) => {
/** Collect DOMAIN-SUFFIX from non_ip/reject.conf for deduplication */ /** Collect DOMAIN-SUFFIX from non_ip/reject.conf for deduplication */
const domainSuffixSet = new Set<string>(); const domainSuffixSet = new Set<string>();
/** Collect DOMAIN-KEYWORD from non_ip/reject.conf for deduplication */ /** Collect DOMAIN-KEYWORD from non_ip/reject.conf for deduplication */
const domainKeywordsSet = new Set<string>(); const domainKeywordsSet = new Set<string>();
await childSpan.traceChild('collect keywords/suffixes').traceAsyncFn(async () => { await childSpan.traceChildAsync('collect keywords/suffixes', async () => {
for await (const line of readFileByLine(path.resolve(import.meta.dir, '../Source/non_ip/reject.conf'))) { for await (const line of readFileByLine(path.resolve(import.meta.dir, '../Source/non_ip/reject.conf'))) {
const [type, value] = line.split(','); const [type, value] = line.split(',');
@ -106,7 +106,7 @@ export const buildRejectDomainSet = task(import.meta.path, async (span) => {
SetHelpers.subtract(domainSets, domainSuffixSet); SetHelpers.subtract(domainSets, domainSuffixSet);
SetHelpers.subtract(domainSets, filterRuleWhitelistDomainSets); SetHelpers.subtract(domainSets, filterRuleWhitelistDomainSets);
childSpan.traceChild('dedupe from white/suffixes').traceSyncFn(() => { childSpan.traceChildSync('dedupe from white/suffixes', () => {
const trie = createTrie(domainSets); const trie = createTrie(domainSets);
domainSuffixSet.forEach(suffix => { domainSuffixSet.forEach(suffix => {
@ -126,7 +126,7 @@ export const buildRejectDomainSet = task(import.meta.path, async (span) => {
}); });
}); });
childSpan.traceChild('dedupe from black keywords').traceSyncFn(() => { childSpan.traceChildSync('dedupe from black keywords', () => {
const kwfilter = createKeywordFilter(domainKeywordsSet); const kwfilter = createKeywordFilter(domainKeywordsSet);
for (const domain of domainSets) { for (const domain of domainSets) {
@ -142,7 +142,7 @@ export const buildRejectDomainSet = task(import.meta.path, async (span) => {
previousSize = domainSets.size; previousSize = domainSets.size;
// Dedupe domainSets // Dedupe domainSets
const dudupedDominArray = span.traceChild('dedupe from covered subdomain').traceSyncFn(() => domainDeduper(Array.from(domainSets))); const dudupedDominArray = span.traceChildSync('dedupe from covered subdomain', () => domainDeduper(Array.from(domainSets)));
console.log(`Deduped ${previousSize - dudupedDominArray.length} rules from covered subdomain!`); console.log(`Deduped ${previousSize - dudupedDominArray.length} rules from covered subdomain!`);
console.log(`Final size ${dudupedDominArray.length}`); console.log(`Final size ${dudupedDominArray.length}`);
@ -186,7 +186,7 @@ export const buildRejectDomainSet = task(import.meta.path, async (span) => {
'Sukka\'s Ruleset - Reject Base', 'Sukka\'s Ruleset - Reject Base',
description, description,
new Date(), new Date(),
span.traceChild('sort reject domainset').traceSyncFn(() => sortDomains(dudupedDominArray, gorhill)), span.traceChildSync('sort reject domainset', () => sortDomains(dudupedDominArray, gorhill)),
'domainset', 'domainset',
path.resolve(import.meta.dir, '../List/domainset/reject.conf'), path.resolve(import.meta.dir, '../List/domainset/reject.conf'),
path.resolve(import.meta.dir, '../Clash/domainset/reject.txt') path.resolve(import.meta.dir, '../Clash/domainset/reject.txt')

View File

@ -175,7 +175,7 @@ export const buildSpeedtestDomainSet = task(import.meta.path, async (span) => {
'.backend.librespeed.org' '.backend.librespeed.org'
]); ]);
await span.traceChild('fetch previous speedtest domainset').traceAsyncFn(async () => { await span.traceChildAsync('fetch previous speedtest domainset', async () => {
SetHelpers.add(domains, await getPreviousSpeedtestDomainsPromise()); SetHelpers.add(domains, await getPreviousSpeedtestDomainsPromise());
}); });
@ -211,7 +211,7 @@ export const buildSpeedtestDomainSet = task(import.meta.path, async (span) => {
'Brazil', 'Brazil',
'Turkey' 'Turkey'
]).reduce<Record<string, Promise<void>>>((pMap, keyword) => { ]).reduce<Record<string, Promise<void>>>((pMap, keyword) => {
pMap[keyword] = span.traceChild(`fetch speedtest endpoints: ${keyword}`).traceAsyncFn(() => querySpeedtestApi(keyword)).then(hostnameGroup => { pMap[keyword] = span.traceChildAsync(`fetch speedtest endpoints: ${keyword}`, () => querySpeedtestApi(keyword)).then(hostnameGroup => {
hostnameGroup.forEach(hostname => { hostnameGroup.forEach(hostname => {
if (hostname) { if (hostname) {
domains.add(hostname); domains.add(hostname);
@ -238,7 +238,7 @@ export const buildSpeedtestDomainSet = task(import.meta.path, async (span) => {
}); });
const gorhill = await getGorhillPublicSuffixPromise(); const gorhill = await getGorhillPublicSuffixPromise();
const deduped = span.traceChild('sort result').traceSyncFn(() => sortDomains(domainDeduper(Array.from(domains)), gorhill)); const deduped = span.traceChildSync('sort result', () => sortDomains(domainDeduper(Array.from(domains)), gorhill));
const description = [ const description = [
...SHARED_DESCRIPTION, ...SHARED_DESCRIPTION,

View File

@ -9,7 +9,7 @@ import { ALL, NORTH_AMERICA, EU, HK, TW, JP, KR } from '../Source/stream';
import { SHARED_DESCRIPTION } from './lib/constants'; import { SHARED_DESCRIPTION } from './lib/constants';
export const createRulesetForStreamService = (span: Span, fileId: string, title: string, streamServices: Array<import('../Source/stream').StreamService>) => { export const createRulesetForStreamService = (span: Span, fileId: string, title: string, streamServices: Array<import('../Source/stream').StreamService>) => {
return span.traceChild(fileId).traceAsyncFn(async (childSpan) => Promise.all([ return span.traceChildAsync(fileId, async (childSpan) => Promise.all([
// Domains // Domains
createRuleset( createRuleset(
childSpan, childSpan,

View File

@ -36,7 +36,7 @@ export const buildTelegramCIDR = task(import.meta.path, async (span) => {
const promise = getTelegramCIDRPromise(); const promise = getTelegramCIDRPromise();
const peeked = Bun.peek(promise); const peeked = Bun.peek(promise);
const { date, results } = peeked === promise const { date, results } = peeked === promise
? await span.traceChild('get telegram cidr').tracePromise(promise) ? await span.traceChildPromise('get telegram cidr', promise)
: (peeked as { date: Date, results: string[] }); : (peeked as { date: Date, results: string[] });
if (results.length === 0) { if (results.length === 0) {

View File

@ -25,19 +25,17 @@ export interface CacheOptions<S = string> {
type?: S extends string ? 'string' : 'buffer' type?: S extends string ? 'string' : 'buffer'
} }
interface CacheApplyNonStringOption<T, S = string> { interface CacheApplyRawOption {
ttl?: number | null, ttl?: number | null,
temporaryBypass?: boolean
}
interface CacheApplyNonRawOption<T, S> extends CacheApplyRawOption {
serializer: (value: T) => S, serializer: (value: T) => S,
deserializer: (cached: S) => T, deserializer: (cached: S) => T
temporaryBypass?: boolean
} }
interface CacheApplyStringOption { type CacheApplyOption<T, S> = T extends S ? CacheApplyRawOption : CacheApplyNonRawOption<T, S>;
ttl?: number | null,
temporaryBypass?: boolean
}
type CacheApplyOption<T, S = string> = T extends string ? CacheApplyStringOption : CacheApplyNonStringOption<T, S>;
const randomInt = (min: number, max: number) => Math.floor(Math.random() * (max - min + 1)) + min; const randomInt = (min: number, max: number) => Math.floor(Math.random() * (max - min + 1)) + min;
@ -127,8 +125,8 @@ export class Cache<S = string> {
}); });
} }
get(key: string, defaultValue?: string): string | undefined { get(key: string, defaultValue?: S): S | undefined {
const rv = this.db.prepare<{ value: string }, string>( const rv = this.db.prepare<{ value: S }, string>(
`SELECT value FROM ${this.tableName} WHERE key = ? LIMIT 1` `SELECT value FROM ${this.tableName} WHERE key = ? LIMIT 1`
).get(key); ).get(key);
@ -150,7 +148,7 @@ export class Cache<S = string> {
async apply<T>( async apply<T>(
key: string, key: string,
fn: () => Promise<T>, fn: () => Promise<T>,
opt: CacheApplyOption<T> opt: CacheApplyOption<T, S>
): Promise<T> { ): Promise<T> {
const { ttl, temporaryBypass } = opt; const { ttl, temporaryBypass } = opt;

View File

@ -18,7 +18,7 @@ export async function compareAndWriteFile(span: Span, linesA: string[], filePath
console.log(`Nothing to write to ${filePath}...`); console.log(`Nothing to write to ${filePath}...`);
isEqual = false; isEqual = false;
} else { } else {
isEqual = await span.traceChild(`comparing ${filePath}`).traceAsyncFn(async () => { isEqual = await span.traceChildAsync(`comparing ${filePath}`, async () => {
let index = 0; let index = 0;
for await (const lineB of readFileByLine(file)) { for await (const lineB of readFileByLine(file)) {
@ -63,7 +63,7 @@ export async function compareAndWriteFile(span: Span, linesA: string[], filePath
return; return;
} }
await span.traceChild(`writing ${filePath}`).traceAsyncFn(async () => { await span.traceChildAsync(`writing ${filePath}`, async () => {
if (linesALen < 10000) { if (linesALen < 10000) {
return Bun.write(file, `${linesA.join('\n')}\n`); return Bun.write(file, `${linesA.join('\n')}\n`);
} }
@ -98,7 +98,7 @@ export const createRuleset = (
type: 'ruleset' | 'domainset', surgePath: string, clashPath: string type: 'ruleset' | 'domainset', surgePath: string, clashPath: string
) => parentSpan.traceChild(`create ruleset: ${path.basename(surgePath, path.extname(surgePath))}`).traceAsyncFn((childSpan) => { ) => parentSpan.traceChild(`create ruleset: ${path.basename(surgePath, path.extname(surgePath))}`).traceAsyncFn((childSpan) => {
const surgeContent = withBannerArray(title, description, date, content); const surgeContent = withBannerArray(title, description, date, content);
const clashContent = childSpan.traceChild('convert incoming ruleset to clash').traceSyncFn(() => { const clashContent = childSpan.traceChildSync('convert incoming ruleset to clash', () => {
let _clashContent; let _clashContent;
switch (type) { switch (type) {
case 'domainset': case 'domainset':

View File

@ -98,8 +98,8 @@ export const getPhishingDomains = (parentSpan: Span) => parentSpan.traceChild('g
SetAdd(domainSet, domainSet2); SetAdd(domainSet, domainSet2);
} }
span.traceChild('whitelisting phishing domains').traceSyncFn((parentSpan) => { span.traceChildSync('whitelisting phishing domains', (parentSpan) => {
const trieForRemovingWhiteListed = parentSpan.traceChild('create trie for whitelisting').traceSyncFn(() => createTrie(domainSet)); const trieForRemovingWhiteListed = parentSpan.traceChildSync('create trie for whitelisting', () => createTrie(domainSet));
return parentSpan.traceChild('delete whitelisted from domainset').traceSyncFn(() => { return parentSpan.traceChild('delete whitelisted from domainset').traceSyncFn(() => {
for (let i = 0, len = WHITELIST_DOMAIN.length; i < len; i++) { for (let i = 0, len = WHITELIST_DOMAIN.length; i < len; i++) {
@ -115,7 +115,7 @@ export const getPhishingDomains = (parentSpan: Span) => parentSpan.traceChild('g
const domainCountMap: Record<string, number> = {}; const domainCountMap: Record<string, number> = {};
const getDomain = createCachedGorhillGetDomain(gorhill); const getDomain = createCachedGorhillGetDomain(gorhill);
span.traceChild('process phishing domain set').traceSyncFn(() => { span.traceChildSync('process phishing domain set', () => {
const domainArr = Array.from(domainSet); const domainArr = Array.from(domainSet);
for (let i = 0, len = domainArr.length; i < len; i++) { for (let i = 0, len = domainArr.length; i < len; i++) {
@ -177,14 +177,14 @@ export const getPhishingDomains = (parentSpan: Span) => parentSpan.traceChild('g
} }
}); });
const results = span.traceChild('get final phishing results').traceSyncFn(() => { const results = span.traceChildSync('get final phishing results', () => {
const results: string[] = []; const res: string[] = [];
for (const domain in domainCountMap) { for (const domain in domainCountMap) {
if (domainCountMap[domain] >= 5) { if (domainCountMap[domain] >= 5) {
results.push(`.${domain}`); res.push(`.${domain}`);
} }
} }
return results; return res;
}); });
return [results, domainSet] as const; return [results, domainSet] as const;

View File

@ -29,6 +29,9 @@ export interface Span {
readonly traceSyncFn: <T>(fn: (span: Span) => T) => T, readonly traceSyncFn: <T>(fn: (span: Span) => T) => T,
readonly traceAsyncFn: <T>(fn: (span: Span) => T | Promise<T>) => Promise<T>, readonly traceAsyncFn: <T>(fn: (span: Span) => T | Promise<T>) => Promise<T>,
readonly tracePromise: <T>(promise: Promise<T>) => Promise<T>, readonly tracePromise: <T>(promise: Promise<T>) => Promise<T>,
readonly traceChildSync: <T>(name: string, fn: (span: Span) => T) => T,
readonly traceChildAsync: <T>(name: string, fn: (span: Span) => T | Promise<T>) => Promise<T>,
readonly traceChildPromise: <T>(name: string, promise: Promise<T>) => Promise<T>,
readonly traceResult: TraceResult readonly traceResult: TraceResult
} }
@ -91,6 +94,15 @@ export const createSpan = (name: string, parentTraceResult?: TraceResult): Span
} finally { } finally {
span.stop(); span.stop();
} }
},
traceChildSync<T>(name: string, fn: (span: Span) => T): T {
return traceChild(name).traceSyncFn(fn);
},
traceChildAsync<T>(name: string, fn: (span: Span) => T | Promise<T>): Promise<T> {
return traceChild(name).traceAsyncFn(fn);
},
traceChildPromise<T>(name: string, promise: Promise<T>): Promise<T> {
return traceChild(name).tracePromise(promise);
} }
}; };

View File

@ -1,4 +1,4 @@
interface StreamService { export interface StreamService {
name: string, name: string,
rules: string[], rules: string[],
ip?: { ip?: {