Skip to content

Commit a29652f

Browse files
committed
optimize queries
1 parent 2c0d946 commit a29652f

11 files changed

Lines changed: 936 additions & 450 deletions

File tree

src/handlers/getBridgeChains.ts

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { IResponse, successResponse } from "../utils/lambda-response";
22
import wrap from "../utils/wrap";
3-
import { getDailyBridgeVolume } from "../utils/bridgeVolume";
3+
import { getDailyBridgeVolumesByChain } from "../utils/bridgeVolume";
44
import { getChainDisplayName, chainCoingeckoIds } from "../utils/normalizeChain";
55
import { getCurrentUnixTimestamp, secondsInDay } from "../utils/date";
66
import bridgeNetworks from "../data/bridgeNetworkData";
@@ -10,34 +10,30 @@ export async function craftBridgeChainsResponse() {
1010
const chainsMap = new Map<string, string>();
1111
const currentTimestamp = getCurrentUnixTimestamp();
1212

13-
await Promise.all(
14-
bridgeNetworks.map(async (bridgeNetwork) => {
15-
const { chains, destinationChain } = bridgeNetwork;
13+
for (const { chains, destinationChain } of bridgeNetworks) {
14+
if (destinationChain) {
15+
const normalizedName = normalizeChain(destinationChain);
16+
chainsMap.set(normalizedName, destinationChain);
17+
}
1618

17-
if (destinationChain) {
18-
const normalizedName = normalizeChain(destinationChain);
19-
chainsMap.set(normalizedName, destinationChain);
20-
}
19+
chains.forEach((chain) => {
20+
const normalizedName = normalizeChain(chain);
21+
chainsMap.set(normalizedName, chain);
22+
});
23+
}
2124

22-
chains.forEach((chain) => {
23-
const normalizedName = normalizeChain(chain);
24-
chainsMap.set(normalizedName, chain);
25-
});
26-
})
25+
const lastWeekVolumesByChain = await getDailyBridgeVolumesByChain(
26+
currentTimestamp - 7 * secondsInDay,
27+
currentTimestamp
2728
);
28-
29-
const chainPromises = Promise.all(
30-
Array.from(chainsMap.keys()).map(async (normalizedChain) => {
29+
const raw = Array.from(chainsMap.keys())
30+
.map((normalizedChain) => {
3131
const chainName = getChainDisplayName(normalizedChain, true);
3232
if (chainCoingeckoIds[chainName] === undefined) {
3333
return;
3434
}
3535

36-
const lastWeekDailyBridgeVolume = await getDailyBridgeVolume(
37-
currentTimestamp - 7 * secondsInDay,
38-
currentTimestamp,
39-
normalizedChain
40-
);
36+
const lastWeekDailyBridgeVolume = lastWeekVolumesByChain[normalizedChain] ?? [];
4137

4238
let volumePrevDay = 0;
4339
if (lastWeekDailyBridgeVolume.length > 1) {
@@ -52,8 +48,7 @@ export async function craftBridgeChainsResponse() {
5248
name: chainName,
5349
};
5450
})
55-
);
56-
const raw = (await chainPromises).filter((chain) => chain) as Array<{
51+
.filter((chain) => chain) as Array<{
5752
gecko_id: string | null;
5853
volumePrevDay: number;
5954
tokenSymbol: string | null;

src/handlers/getBridgeStatsOnDay.ts

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ import { IResponse, successResponse, errorResponse } from "../utils/lambda-respo
22
import wrap from "../utils/wrap";
33
import { getCurrentUnixTimestamp, getTimestampAtStartOfDay } from "../utils/date";
44
import {
5-
queryAggregatedTokenStatsTop30,
6-
queryAggregatedTokenStatsTop30Rolling,
7-
queryAggregatedTotalsRolling,
8-
queryAggregatedTotalsTimestampRange,
5+
queryAggregatedStatsTop30,
6+
queryAggregatedStatsTop30Rolling,
97
} from "../utils/wrappa/postgres/query";
108
import { getLlamaPrices } from "../utils/prices";
119
import { importBridgeNetwork } from "../data/importBridgeNetwork";
@@ -84,17 +82,12 @@ const getBridgeStatsOnDay = async (
8482
const currentTimestamp = getCurrentUnixTimestamp();
8583
const endTimestamp = Math.max(queryTimestamp, Math.min(maxEndTimestamp, currentTimestamp));
8684

87-
const [rows, totals] = (await Promise.all(
88-
rollingHours
89-
? [
90-
queryAggregatedTokenStatsTop30Rolling(rollingHours, queryChain, bridgeDbName),
91-
queryAggregatedTotalsRolling(rollingHours, queryChain, bridgeDbName),
92-
]
93-
: [
94-
queryAggregatedTokenStatsTop30(queryTimestamp, endTimestamp, queryChain, bridgeDbName),
95-
queryAggregatedTotalsTimestampRange(queryTimestamp, endTimestamp, queryChain, bridgeDbName),
96-
]
97-
)) as [StatsRow[], StatsTotals];
85+
const { rows, totals } = (rollingHours
86+
? await queryAggregatedStatsTop30Rolling(rollingHours, queryChain, bridgeDbName)
87+
: await queryAggregatedStatsTop30(queryTimestamp, endTimestamp, queryChain, bridgeDbName)) as {
88+
rows: StatsRow[];
89+
totals: StatsTotals;
90+
};
9891

9992
const dt = rows.filter((r) => r.kind === "dt");
10093
const wt = rows.filter((r) => r.kind === "wt");

src/handlers/getBridges.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { IResponse, successResponse } from "../utils/lambda-response";
22
import wrap from "../utils/wrap";
3-
import { getDailyBridgeVolume } from "../utils/bridgeVolume";
3+
import { getDailyBridgeVolumesByBridge } from "../utils/bridgeVolume";
44
import { craftBridgeChainsResponse } from "./getBridgeChains";
55
import { secondsInDay, getCurrentUnixTimestamp, getTimestampAtStartOfDay } from "../utils/date";
66
import bridgeNetworks from "../data/bridgeNetworkData";
@@ -11,18 +11,18 @@ const getBridges = async () => {
1111
const startOfTheDayTs = getTimestampAtStartOfDay(getCurrentUnixTimestamp());
1212
const dailyStartTimestamp = startOfTheDayTs - 30 * secondsInDay;
1313

14-
const [all24hVolumes, ...allMonthlyVolumes] = await Promise.all([
14+
const [all24hVolumes, monthlyVolumesByBridge] = await Promise.all([
1515
getAllLast24HVolumes(),
16-
...bridgeNetworks.map(({ id }) => getDailyBridgeVolume(dailyStartTimestamp, startOfTheDayTs, undefined, id)),
16+
getDailyBridgeVolumesByBridge(dailyStartTimestamp, startOfTheDayTs),
1717
]);
1818

1919
const response = bridgeNetworks
20-
.map((bridgeNetwork, i) => {
20+
.map((bridgeNetwork) => {
2121
const { id, bridgeDbName, url, displayName, iconLink, chains, destinationChain, slug, defillamaId } =
2222
bridgeNetwork;
2323

2424
const last24hVolume = all24hVolumes[bridgeDbName] ?? 0;
25-
const lastMonthDailyVolume = allMonthlyVolumes[i];
25+
const lastMonthDailyVolume = monthlyVolumesByBridge[bridgeDbName] ?? [];
2626

2727
let lastDailyVolume = 0;
2828
let dayBeforeLastVolume = 0;

src/server/health.ts

Lines changed: 52 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,71 @@
1-
import { cpuUsage } from "process";
2-
import { cpus, freemem, totalmem, hostname, loadavg } from "os";
31
import { querySql as sql } from "../utils/db";
2+
import { checkRedisConnectivity } from "../utils/cache";
43

5-
const CPU_HISTORY_HOURS = 24;
6-
const cpuHistory: Array<{ timestamp: string; usage: string }> = [];
4+
const DEPENDENCY_CHECK_INTERVAL_MS = 5 * 60 * 1000;
5+
const DB_CHECK_TIMEOUT_MS = 1500;
76

8-
function formatBytes(bytes: number) {
9-
const gb = bytes / (1024 * 1024 * 1024);
10-
return `${gb.toFixed(2)} GB`;
11-
}
7+
type DependencyResult = {
8+
status: "OK" | "ERROR" | "DISABLED" | "UNKNOWN";
9+
latencyMs?: number;
10+
checkedAt: string;
11+
};
1212

13-
function formatUptime(seconds: number) {
14-
const days = Math.floor(seconds / (24 * 60 * 60));
15-
const hours = Math.floor((seconds % (24 * 60 * 60)) / (60 * 60));
16-
const minutes = Math.floor((seconds % (60 * 60)) / 60);
17-
return `${days}d ${hours}h ${minutes}m`;
18-
}
13+
export type DependencyHealth = {
14+
db: DependencyResult;
15+
redis: DependencyResult;
16+
checkedAt: string;
17+
};
1918

20-
function updateCpuHistory() {
21-
const startUsage = cpuUsage();
19+
const neverChecked = new Date(0).toISOString();
20+
let dependencyHealth: DependencyHealth = {
21+
db: { status: "UNKNOWN", checkedAt: neverChecked },
22+
redis: { status: process.env.REDIS_URL ? "UNKNOWN" : "DISABLED", checkedAt: neverChecked },
23+
checkedAt: neverChecked,
24+
};
25+
let dependencyCheck: Promise<void> | undefined;
26+
let monitoringStarted = false;
2227

23-
setTimeout(() => {
24-
const endUsage = cpuUsage(startUsage);
25-
const totalUsage = endUsage.user + endUsage.system;
26-
const usagePercent = (totalUsage / 1000000 / 3600).toFixed(1);
28+
const checkDependencies = () => {
29+
if (dependencyCheck) return dependencyCheck;
2730

28-
cpuHistory.push({
29-
timestamp: new Date().toISOString(),
30-
usage: `${usagePercent}%`,
31-
});
31+
dependencyCheck = (async () => {
32+
const startedAt = Date.now();
33+
const dbCheck = Promise.race([
34+
Promise.resolve(sql`SELECT 1`).then(() => ({ status: "OK" as const, latencyMs: Date.now() - startedAt })),
35+
new Promise<{ status: "ERROR"; latencyMs: number }>((resolve) =>
36+
setTimeout(() => resolve({ status: "ERROR", latencyMs: Date.now() - startedAt }), DB_CHECK_TIMEOUT_MS)
37+
),
38+
]).catch(() => ({ status: "ERROR" as const, latencyMs: Date.now() - startedAt }));
3239

33-
if (cpuHistory.length > CPU_HISTORY_HOURS) {
34-
cpuHistory.shift();
35-
}
36-
}, 1000);
37-
}
40+
const [db, redis] = await Promise.all([dbCheck, checkRedisConnectivity()]);
41+
const checkedAt = new Date().toISOString();
42+
dependencyHealth = {
43+
db: { ...db, checkedAt },
44+
redis: { ...redis, checkedAt },
45+
checkedAt,
46+
};
47+
})().finally(() => {
48+
dependencyCheck = undefined;
49+
});
50+
51+
return dependencyCheck;
52+
};
3853

3954
export function startHealthMonitoring() {
40-
setInterval(updateCpuHistory, 3600000);
41-
updateCpuHistory();
55+
if (monitoringStarted) return;
56+
monitoringStarted = true;
57+
void checkDependencies();
58+
const interval = setInterval(() => void checkDependencies(), DEPENDENCY_CHECK_INTERVAL_MS);
59+
interval.unref?.();
4260
}
4361

44-
export async function checkDbConnectivity(): Promise<{ status: "OK" | "ERROR"; latencyMs?: number }> {
45-
const start = Date.now();
46-
try {
47-
await sql`SELECT 1`;
48-
return { status: "OK", latencyMs: Date.now() - start };
49-
} catch {
50-
return { status: "ERROR" };
51-
}
62+
export function getDependencyHealth() {
63+
return dependencyHealth;
5264
}
5365

5466
export function getHealthStatus() {
55-
const memoryTotal = totalmem();
56-
const memoryFree = freemem();
57-
const memoryUsed = memoryTotal - memoryFree;
58-
const memoryUsagePercent = ((memoryUsed / memoryTotal) * 100).toFixed(1);
59-
60-
const [oneMin, fiveMin, fifteenMin] = loadavg();
61-
const cpuCount = cpus().length;
62-
63-
const health = {
67+
return {
6468
status: "OK",
6569
timestamp: new Date().toISOString(),
66-
server: {
67-
hostname: hostname(),
68-
uptime: formatUptime(process.uptime()),
69-
},
70-
memory: {
71-
total: formatBytes(memoryTotal),
72-
used: formatBytes(memoryUsed),
73-
free: formatBytes(memoryFree),
74-
usage: `${memoryUsagePercent}%`,
75-
status: Number(memoryUsagePercent) > 90 ? "WARNING" : "OK",
76-
},
77-
cpu: {
78-
cores: cpuCount,
79-
load: {
80-
"1min": ((oneMin / cpuCount) * 100).toFixed(1) + "%",
81-
"5min": ((fiveMin / cpuCount) * 100).toFixed(1) + "%",
82-
"15min": ((fifteenMin / cpuCount) * 100).toFixed(1) + "%",
83-
},
84-
history: cpuHistory,
85-
status: oneMin / cpuCount > 0.8 ? "WARNING" : "OK",
86-
},
8770
};
88-
89-
const statusCode = health.memory.status === "WARNING" || health.cpu.status === "WARNING" ? 207 : 200;
90-
91-
return { health, statusCode };
9271
}

0 commit comments

Comments
 (0)