Skip to content

Commit

Permalink
Improve error handling in api (#383)
Browse files Browse the repository at this point in the history
  • Loading branch information
Redm4x authored Sep 24, 2024
1 parent 4a43483 commit 8dfc23e
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 13 deletions.
19 changes: 13 additions & 6 deletions apps/api/src/caching/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import * as Sentry from "@sentry/node";
import { differenceInSeconds } from "date-fns";

import { LoggerService } from "@src/core";
import MemoryCacheEngine from "./memoryCacheEngine";

const logger = new LoggerService({ context: "Caching" });

export const cacheEngine = new MemoryCacheEngine();
const pendingRequests: { [key: string]: Promise<unknown> } = {};

Expand Down Expand Up @@ -30,20 +33,24 @@ export const Memoize = (options?: MemoizeOptions) => (target: object, propertyNa
export async function cacheResponse<T>(seconds: number, key: string, refreshRequest: () => Promise<T>, keepData?: boolean): Promise<T> {
const duration = seconds * 1000;
const cachedObject = cacheEngine.getFromCache(key) as CachedObject<T> | undefined;
// console.log(`Cache key: ${key}`);
logger.debug(`Request for key: ${key}`);

// If first time or expired, must refresh data if not already refreshing
const cacheExpired = Math.abs(differenceInSeconds(cachedObject?.date, new Date())) > seconds;
if ((!cachedObject || cacheExpired) && !(key in pendingRequests)) {
// console.log(`Making request: ${key}`);
logger.debug(`Object was not in cache or is expired, making new request for key: ${key}`);
pendingRequests[key] = refreshRequest()
.then(data => {
cacheEngine.storeInCache(key, { date: new Date(), data: data }, keepData ? undefined : duration);
return data;
})
.catch(err => {
console.error(`Error making cache request ${err}`);
Sentry.captureException(err);
if (cachedObject) {
logger.error(`Error making cache request ${err}`);
Sentry.captureException(err);
} else {
throw err;
}
})
.finally(() => {
delete pendingRequests[key];
Expand All @@ -52,10 +59,10 @@ export async function cacheResponse<T>(seconds: number, key: string, refreshRequ

// If there is data in cache, return it even if it is expired. Otherwise, wait for the refresh request to finish
if (cachedObject) {
// console.log(`Cache hit: ${key}`);
logger.debug(`Returning cached object for key: ${key}`);
return cachedObject.data;
} else {
// console.log(`Waiting for pending request: ${key}`);
logger.debug(`Waiting for pending request for key: ${key}`);
return (await pendingRequests[key]) as T;
}
}
Expand Down
16 changes: 15 additions & 1 deletion apps/api/src/routes/v1/addresses/deployments.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";

import { getAddressDeployments } from "@src/services/external/apiNodeService";
import { isValidBech32Address } from "@src/utils/addresses";
import { openApiExampleAddress } from "@src/utils/constants";

const maxLimit = 100;
Expand Down Expand Up @@ -59,15 +60,28 @@ const route = createRoute({
})
}
}
},
400: {
description: "Invalid address"
}
}
});

export default new OpenAPIHono().openapi(route, async c => {
if (!isValidBech32Address(c.req.valid("param").address, "akash")) {
return c.text("Invalid address", 400);
}

const skip = parseInt(c.req.valid("param").skip);
const limit = Math.min(maxLimit, parseInt(c.req.valid("param").limit));

// TODO Add param validation
if (isNaN(skip)) {
return c.text("Invalid skip.", 400);
}

if (isNaN(limit)) {
return c.text("Invalid limit.", 400);
}

const deployments = await getAddressDeployments(c.req.valid("param").address, skip, limit, c.req.valid("query").reverseSorting === "true", {
status: c.req.valid("query").status
Expand Down
19 changes: 13 additions & 6 deletions apps/api/src/services/external/githubService.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Octokit } from "@octokit/rest";
import axios from "axios";
import minutesToSeconds from "date-fns/minutesToSeconds";

import { cacheKeys, cacheResponse } from "@src/caching/helpers";
import { Auditor, ProviderAttributesSchema } from "@src/types/provider";
Expand All @@ -22,19 +23,25 @@ export function getOctokit() {
export const getProviderAttributesSchema = async (): Promise<ProviderAttributesSchema> => {
// Fetching provider attributes schema
const response = await cacheResponse(
30,
minutesToSeconds(5),
cacheKeys.getProviderAttributesSchema,
async () => await axios.get<ProviderAttributesSchema>("https://raw.githubusercontent.com/akash-network/console/main/config/provider-attributes.json")
async () => await axios.get<ProviderAttributesSchema>("https://raw.githubusercontent.com/akash-network/console/main/config/provider-attributes.json"),
true
);

return response.data;
};

export async function getAuditors() {
const response = await cacheResponse(60 * 5, cacheKeys.getAuditors, async () => {
const res = await axios.get<Auditor[]>("https://raw.githubusercontent.com/akash-network/console/main/config/auditors.json");
return res.data;
});
const response = await cacheResponse(
minutesToSeconds(5),
cacheKeys.getAuditors,
async () => {
const res = await axios.get<Auditor[]>("https://raw.githubusercontent.com/akash-network/console/main/config/auditors.json");
return res.data;
},
true
);

return response;
}

0 comments on commit 8dfc23e

Please sign in to comment.