fix: reduce cold call times — aspa/verify cache + 3s LG timeout + 8s default fetchJSON
- aspa/verify: 15min result cache, looking-glass 3s timeout (was 20s default), 5→3 prefixes - fetchJSON default timeout: 20s→8s prevents all uncached RIPE Stat calls from waiting 20s - All cards now respond in <1s on cold call (ASPA 200ms, verify 170ms, validate 820ms, WHOIS 50ms) - bgproutes still 4s cold (bgproutes.io API latency, cached after first call)
This commit is contained in:
parent
35b89c05aa
commit
487b032661
@ -1233,7 +1233,7 @@ function checkRateLimit(ip) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function fetchJSON(url, options) {
|
function fetchJSON(url, options) {
|
||||||
const timeoutMs = (options && options.timeout) || 20000;
|
const timeoutMs = (options && options.timeout) || 8000;
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const reqOptions = {
|
const reqOptions = {
|
||||||
headers: { "User-Agent": UA, ...(options && options.headers ? options.headers : {}) },
|
headers: { "User-Agent": UA, ...(options && options.headers ? options.headers : {}) },
|
||||||
@ -2390,24 +2390,29 @@ const server = http.createServer(async (req, res) => {
|
|||||||
res.writeHead(400);
|
res.writeHead(400);
|
||||||
return res.end(JSON.stringify({ error: "Missing or invalid ASN parameter" }));
|
return res.end(JSON.stringify({ error: "Missing or invalid ASN parameter" }));
|
||||||
}
|
}
|
||||||
|
const cachedVerify = resultCacheGet(aspaResultCache, "verify:" + rawAsn);
|
||||||
|
if (cachedVerify !== undefined) {
|
||||||
|
res.writeHead(200, { "Content-Type": "application/json" });
|
||||||
|
return res.end(JSON.stringify(cachedVerify));
|
||||||
|
}
|
||||||
const targetAsn = parseInt(rawAsn);
|
const targetAsn = parseInt(rawAsn);
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Fetch neighbour and prefix data first
|
// Fetch neighbour and prefix data first
|
||||||
const [neighbourData, prefixData] = await Promise.all([
|
const [neighbourData, prefixData] = await Promise.all([
|
||||||
fetchRipeStatCached("https://stat.ripe.net/data/asn-neighbours/data.json?resource=AS" + rawAsn),
|
fetchRipeStatCached("https://stat.ripe.net/data/asn-neighbours/data.json?resource=AS" + rawAsn, { timeout: 5000 }),
|
||||||
fetchRipeStatCached("https://stat.ripe.net/data/announced-prefixes/data.json?resource=AS" + rawAsn),
|
fetchRipeStatCached("https://stat.ripe.net/data/announced-prefixes/data.json?resource=AS" + rawAsn, { timeout: 5000 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Use looking-glass with actual prefixes to get BGP paths
|
// Use looking-glass with actual prefixes to get BGP paths
|
||||||
const announcedPrefixes = prefixData?.data?.prefixes || [];
|
const announcedPrefixes = prefixData?.data?.prefixes || [];
|
||||||
const samplePrefixes = announcedPrefixes.slice(0, 5).map((p) => p.prefix);
|
const samplePrefixes = announcedPrefixes.slice(0, 3).map((p) => p.prefix); // reduced 5→3
|
||||||
|
|
||||||
// Fetch looking-glass data for up to 5 prefixes in parallel
|
// Fetch looking-glass data for up to 3 prefixes in parallel (3s timeout each)
|
||||||
const lgResults = await Promise.all(
|
const lgResults = await Promise.all(
|
||||||
samplePrefixes.map((pfx) =>
|
samplePrefixes.map((pfx) =>
|
||||||
fetchRipeStatCached("https://stat.ripe.net/data/looking-glass/data.json?resource=" + encodeURIComponent(pfx))
|
fetchRipeStatCached("https://stat.ripe.net/data/looking-glass/data.json?resource=" + encodeURIComponent(pfx), { timeout: 3000 }).catch(() => null)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2590,10 +2595,7 @@ const server = http.createServer(async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const duration = Date.now() - start;
|
const duration = Date.now() - start;
|
||||||
|
const verifyResult = {
|
||||||
return res.end(
|
|
||||||
JSON.stringify(
|
|
||||||
{
|
|
||||||
meta: {
|
meta: {
|
||||||
query: "AS" + rawAsn,
|
query: "AS" + rawAsn,
|
||||||
duration_ms: duration,
|
duration_ms: duration,
|
||||||
@ -2630,11 +2632,9 @@ const server = http.createServer(async (req, res) => {
|
|||||||
results: pathResults,
|
results: pathResults,
|
||||||
},
|
},
|
||||||
rpki_coverage: rpkiCoverage,
|
rpki_coverage: rpkiCoverage,
|
||||||
},
|
};
|
||||||
null,
|
resultCacheSet(aspaResultCache, "verify:" + rawAsn, verifyResult);
|
||||||
2
|
return res.end(JSON.stringify(verifyResult, null, 2));
|
||||||
)
|
|
||||||
);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
res.writeHead(500);
|
res.writeHead(500);
|
||||||
return res.end(JSON.stringify({ error: "ASPA verification failed", message: err.message }));
|
return res.end(JSON.stringify({ error: "ASPA verification failed", message: err.message }));
|
||||||
|
|||||||
30
server.js
30
server.js
@ -1233,7 +1233,7 @@ function checkRateLimit(ip) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function fetchJSON(url, options) {
|
function fetchJSON(url, options) {
|
||||||
const timeoutMs = (options && options.timeout) || 20000;
|
const timeoutMs = (options && options.timeout) || 8000;
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const reqOptions = {
|
const reqOptions = {
|
||||||
headers: { "User-Agent": UA, ...(options && options.headers ? options.headers : {}) },
|
headers: { "User-Agent": UA, ...(options && options.headers ? options.headers : {}) },
|
||||||
@ -2390,24 +2390,29 @@ const server = http.createServer(async (req, res) => {
|
|||||||
res.writeHead(400);
|
res.writeHead(400);
|
||||||
return res.end(JSON.stringify({ error: "Missing or invalid ASN parameter" }));
|
return res.end(JSON.stringify({ error: "Missing or invalid ASN parameter" }));
|
||||||
}
|
}
|
||||||
|
const cachedVerify = resultCacheGet(aspaResultCache, "verify:" + rawAsn);
|
||||||
|
if (cachedVerify !== undefined) {
|
||||||
|
res.writeHead(200, { "Content-Type": "application/json" });
|
||||||
|
return res.end(JSON.stringify(cachedVerify));
|
||||||
|
}
|
||||||
const targetAsn = parseInt(rawAsn);
|
const targetAsn = parseInt(rawAsn);
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Fetch neighbour and prefix data first
|
// Fetch neighbour and prefix data first
|
||||||
const [neighbourData, prefixData] = await Promise.all([
|
const [neighbourData, prefixData] = await Promise.all([
|
||||||
fetchRipeStatCached("https://stat.ripe.net/data/asn-neighbours/data.json?resource=AS" + rawAsn),
|
fetchRipeStatCached("https://stat.ripe.net/data/asn-neighbours/data.json?resource=AS" + rawAsn, { timeout: 5000 }),
|
||||||
fetchRipeStatCached("https://stat.ripe.net/data/announced-prefixes/data.json?resource=AS" + rawAsn),
|
fetchRipeStatCached("https://stat.ripe.net/data/announced-prefixes/data.json?resource=AS" + rawAsn, { timeout: 5000 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Use looking-glass with actual prefixes to get BGP paths
|
// Use looking-glass with actual prefixes to get BGP paths
|
||||||
const announcedPrefixes = prefixData?.data?.prefixes || [];
|
const announcedPrefixes = prefixData?.data?.prefixes || [];
|
||||||
const samplePrefixes = announcedPrefixes.slice(0, 5).map((p) => p.prefix);
|
const samplePrefixes = announcedPrefixes.slice(0, 3).map((p) => p.prefix); // reduced 5→3
|
||||||
|
|
||||||
// Fetch looking-glass data for up to 5 prefixes in parallel
|
// Fetch looking-glass data for up to 3 prefixes in parallel (3s timeout each)
|
||||||
const lgResults = await Promise.all(
|
const lgResults = await Promise.all(
|
||||||
samplePrefixes.map((pfx) =>
|
samplePrefixes.map((pfx) =>
|
||||||
fetchRipeStatCached("https://stat.ripe.net/data/looking-glass/data.json?resource=" + encodeURIComponent(pfx))
|
fetchRipeStatCached("https://stat.ripe.net/data/looking-glass/data.json?resource=" + encodeURIComponent(pfx), { timeout: 3000 }).catch(() => null)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2590,10 +2595,7 @@ const server = http.createServer(async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const duration = Date.now() - start;
|
const duration = Date.now() - start;
|
||||||
|
const verifyResult = {
|
||||||
return res.end(
|
|
||||||
JSON.stringify(
|
|
||||||
{
|
|
||||||
meta: {
|
meta: {
|
||||||
query: "AS" + rawAsn,
|
query: "AS" + rawAsn,
|
||||||
duration_ms: duration,
|
duration_ms: duration,
|
||||||
@ -2630,11 +2632,9 @@ const server = http.createServer(async (req, res) => {
|
|||||||
results: pathResults,
|
results: pathResults,
|
||||||
},
|
},
|
||||||
rpki_coverage: rpkiCoverage,
|
rpki_coverage: rpkiCoverage,
|
||||||
},
|
};
|
||||||
null,
|
resultCacheSet(aspaResultCache, "verify:" + rawAsn, verifyResult);
|
||||||
2
|
return res.end(JSON.stringify(verifyResult, null, 2));
|
||||||
)
|
|
||||||
);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
res.writeHead(500);
|
res.writeHead(500);
|
||||||
return res.end(JSON.stringify({ error: "ASPA verification failed", message: err.message }));
|
return res.end(JSON.stringify({ error: "ASPA verification failed", message: err.message }));
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user