Skip to content

Commit

Permalink
feat: add deposit status cache
Browse files Browse the repository at this point in the history
  • Loading branch information
amateima committed Dec 5, 2024
1 parent c168c0a commit 0837d5d
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 22 deletions.
2 changes: 1 addition & 1 deletion packages/indexer-api/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export async function Main(
);

const allRouters: Record<string, Router> = {
deposits: routers.deposits.getRouter(postgres),
deposits: routers.deposits.getRouter(postgres, redis),
balances: routers.balances.getRouter(redis),
statsPage: routers.statsPage.getRouter(postgres),
webhook: webhooks.router,
Expand Down
5 changes: 3 additions & 2 deletions packages/indexer-api/src/routers/deposits.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import Redis from "ioredis";
import { Router } from "express";
import { DataSource } from "@repo/indexer-database";
import { DepositsController } from "../controllers/deposits";
import { DepositsService } from "../services/deposits";

export function getRouter(db: DataSource): Router {
export function getRouter(db: DataSource, redis: Redis): Router {
const router = Router();
const service = new DepositsService(db);
const service = new DepositsService(db, redis);
const controller = new DepositsController(service);
router.get("/deposits", controller.getDeposits);
router.get("/deposit/status", controller.getDepositStatus);
Expand Down
121 changes: 102 additions & 19 deletions packages/indexer-api/src/services/deposits.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { JSON } from "../types";
import { Redis } from "ioredis";
import { DataSource, entities } from "@repo/indexer-database";
import type { DepositParams, DepositsParams } from "../dtos/deposits.dto";
import {
DepositNotFoundException,
IncorrectQueryParamsException,
IndexParamOutOfRangeException,
} from "./exceptions";

Expand All @@ -11,7 +12,10 @@ type APIHandler = (
) => Promise<JSON> | JSON | never | Promise<never> | void | Promise<void>;

export class DepositsService {
constructor(private db: DataSource) {}
constructor(
private db: DataSource,
private redis: Redis,
) {}

public async getDeposits(
params: DepositsParams,
Expand Down Expand Up @@ -95,6 +99,27 @@ export class DepositsService {
}

public async getDepositStatus(params: DepositParams) {
// in the validation rules each of these params are marked as optional
// but we need to check that at least one of them is present
if (
!(
(params.depositId && params.originChainId) ||
params.depositTxHash ||
params.relayDataHash
)
) {
throw new IncorrectQueryParamsException();
}

// construct cache key
const cacheKey = this.getDepositStatusCacheKey(params);
const cachedData = await this.redis.get(cacheKey);

if (cachedData) {
return JSON.parse(cachedData);
}

// no cached data, so we need to query the database
const repo = this.db.getRepository(entities.RelayHashInfo);
const queryBuilder = repo.createQueryBuilder("rhi");

Expand Down Expand Up @@ -123,26 +148,84 @@ export class DepositsService {
const matchingRelays = await queryBuilder.getMany();
const numberMatchingRelays = matchingRelays.length;
if (numberMatchingRelays === 0) throw new DepositNotFoundException();
if (params.index < numberMatchingRelays) {
const relay = matchingRelays[params.index];
const result = {
status: relay?.status,
originChainId: relay?.originChainId,
depositId: relay?.depositId,
depositTxHash: relay?.depositTxHash,
fillTx: relay?.fillTxHash,
destinationChainId: relay?.destinationChainId,
depositRefundTxHash: relay?.depositRefundTxHash,
pagination: {
currentIndex: params.index,
maxIndex: numberMatchingRelays - 1,
},
};
return result;
} else {
const relay = matchingRelays[params.index];
if (!relay) {
throw new IndexParamOutOfRangeException(
`Index ${params.index} out of range. Index must be between 0 and ${numberMatchingRelays - 1}`,
);
}

const result = {
status: relay.status,
originChainId: relay.originChainId,
depositId: relay.depositId,
depositTxHash: relay.depositTxHash,
fillTx: relay.fillTxHash,
destinationChainId: relay.destinationChainId,
depositRefundTxHash: relay.depositRefundTxHash,
pagination: {
currentIndex: params.index,
maxIndex: numberMatchingRelays - 1,
},
};

if (this.shouldCacheDepositStatusResponse(relay.status)) {
await this.redis.set(
cacheKey,
JSON.stringify(result),
"EX",
this.getDepositStatusCacheTTLSeconds(relay.status),
);
}
return result;
}

private getDepositStatusCacheTTLSeconds(status: entities.RelayStatus) {
const minute = 60;
const hour = 60 * minute;
const day = 24 * hour;

switch (status) {
case entities.RelayStatus.Expired:
return minute;
case entities.RelayStatus.Filled:
return day;
case entities.RelayStatus.Refunded:
return day;
case entities.RelayStatus.SlowFillRequested:
return minute * 5;
case entities.RelayStatus.SlowFilled:
return day;
default:
return 0;
}
}

private shouldCacheDepositStatusResponse(status: entities.RelayStatus) {
return [
entities.RelayStatus.Expired,
entities.RelayStatus.Filled,
entities.RelayStatus.Refunded,
entities.RelayStatus.SlowFillRequested,
entities.RelayStatus.SlowFilled,
].includes(status);
}

private getDepositStatusCacheKey(params: DepositParams) {
if (params.depositId && params.originChainId) {
return `depositStatus-${params.depositId}-${params.originChainId}`;
}
if (params.depositTxHash) {
return `depositStatus-${params.depositTxHash}-${params.index}`;
}
if (params.relayDataHash) {
return `depositStatus-${params.relayDataHash}`;
}

// in theory this should never happen because we have already checked
// that at least one of the params is present
throw new Error(
"Could not get deposit status: could not locate cache data",
);
}
}
10 changes: 10 additions & 0 deletions packages/indexer-api/src/services/exceptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,13 @@ export class IndexParamOutOfRangeException extends IndexerHTTPError {
super(StatusCodes.BAD_REQUEST, IndexParamOutOfRangeException.name, message);
}
}

export class IncorrectQueryParamsException extends IndexerHTTPError {
constructor() {
super(
StatusCodes.BAD_REQUEST,
IncorrectQueryParamsException.name,
"Incorrect query params provided",
);
}
}

0 comments on commit 0837d5d

Please sign in to comment.