Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: memory leak #99

Merged
merged 3 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
"@commitlint/config-conventional": "^17.6.7",
"@typechain/ethers-v5": "^11.1.1",
"@types/express": "^4.17.18",
"@types/express-prometheus-middleware": "^1.2.1",
"@types/jest": "^29.5.3",
"@types/node": "^18.16.3",
"@types/node-slack": "^0.0.31",
Expand All @@ -46,7 +45,6 @@
"chalk": "^4.1.2",
"ethers": "^5.7.2",
"express": "^4.18.2",
"express-prometheus-middleware": "^1.2.0",
"graphql-request": "^6.1.0",
"level": "^8.0.0",
"level-ts": "^2.1.0",
Expand Down
2 changes: 1 addition & 1 deletion src/domain/addContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ async function _addContract(
context: ChainContext,
event: ConditionalOrderCreatedEvent
) {
const log = getLogger("addContract");
const log = getLogger("addContract:_addContract");
const composableCow = ComposableCoW__factory.createInterface();
const { provider, registry } = context;
const { transactionHash: tx, blockNumber } = event;
Expand Down
11 changes: 8 additions & 3 deletions src/domain/chainContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ export class ChainContext {
*/
public async warmUp(watchdogTimeout: number, oneShot?: boolean) {
const { provider, chainId } = this;
const log = getLogger(`chainContext:warmUp:${chainId}`);
const log = getLogger("chainContext:warmUp", chainId.toString());
const { lastProcessedBlock } = this.registry;
const { pageSize } = this;

Expand Down Expand Up @@ -272,7 +272,7 @@ export class ChainContext {
lastProcessedBlock: ethers.providers.Block
) {
const { provider, registry, chainId } = this;
const log = getLogger(`chainContext:runBlockWatcher:${chainId}`);
const log = getLogger("chainContext:runBlockWatcher", chainId.toString());
// Watch for new blocks
log.info(`👀 Start block watcher`);
log.debug(`Watchdog timeout: ${watchdogTimeout} seconds`);
Expand Down Expand Up @@ -310,6 +310,7 @@ export class ChainContext {

// Block height metric
this.registry.lastProcessedBlock = blockToRegistryBlock(block);
this.registry.write();
blockHeight.labels(chainId.toString()).set(Number(blockNumber));
} catch {
log.error(`Error processing block ${blockNumber}`);
Expand Down Expand Up @@ -410,7 +411,11 @@ async function processBlock(
const timer = processBlockDurationSeconds
.labels(context.chainId.toString())
.startTimer();
const log = getLogger(`chainContext:processBlock:${chainId}:${block.number}`);
const log = getLogger(
"chainContext:processBlock",
chainId.toString(),
block.number.toString()
);

// Transaction watcher for adding new contracts
let hasErrors = false;
Expand Down
31 changes: 22 additions & 9 deletions src/domain/checkForAndPlaceOrder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,21 @@ export async function checkForAndPlaceOrder(
let ownerCounter = 0;
let orderCounter = 0;

const logPrefix = `checkForAndPlaceOrder:${chainId}:${blockNumber}`;
const log = getLogger(logPrefix);
const log = getLogger(
"checkForAndPlaceOrder:checkForAndPlaceOrder",
chainId.toString(),
blockNumber.toString()
);
log.debug(`Total number of orders: ${numOrders}`);

for (const [owner, conditionalOrders] of ownerOrders.entries()) {
ownerCounter++;
const log = getLogger(`${logPrefix}:${ownerCounter}`);
const log = getLogger(
"checkForAndPlaceOrder:checkForAndPlaceOrder",
chainId.toString(),
blockNumber.toString(),
ownerCounter.toString()
);
const ordersPendingDelete = [];
// enumerate all the `ConditionalOrder`s for a given owner
log.debug(
Expand All @@ -101,7 +109,12 @@ export async function checkForAndPlaceOrder(
orderCounter++;
const ownerRef = `${ownerCounter}.${orderCounter}`;
const orderRef = `${chainId}:${ownerRef}@${blockNumber}`;
const log = getLogger(`${logPrefix}:${ownerRef}}`);
const log = getLogger(
"checkForAndPlaceOrder:checkForAndPlaceOrder",
chainId.toString(),
blockNumber.toString(),
ownerRef
);
const logOrderDetails = `Processing order from TX ${conditionalOrder.tx} with params:`;

const { result: lastHint } = conditionalOrder.pollResult || {};
Expand Down Expand Up @@ -226,7 +239,8 @@ async function _processConditionalOrder(
const { provider, orderBook, dryRun, chainId } = context;
const { handler } = conditionalOrder.params;
const log = getLogger(
`checkForAndPlaceOrder:_processConditionalOrder:${orderRef}`
"checkForAndPlaceOrder:_processConditionalOrder",
orderRef
);
const id = ConditionalOrderSDK.leafToId(conditionalOrder.params);
const metricLabels = [chainId.toString(), handler, owner, id];
Expand Down Expand Up @@ -407,7 +421,7 @@ async function _placeOrder(params: {
dryRun,
metricLabels,
} = params;
const log = getLogger(`checkForAndPlaceOrder:_placeOrder:${orderRef}`);
const log = getLogger("checkForAndPlaceOrder:_placeOrder", orderRef);
const { chainId } = orderBook.context;
try {
const postOrder: OrderCreation = {
Expand Down Expand Up @@ -535,8 +549,7 @@ async function _pollLegacy(
orderRef: string
): Promise<PollResult> {
const { contract, multicall, chainId } = context;
const logPrefix = `checkForAndPlaceOrder:_pollLegacy:${orderRef}`;
const log = getLogger(logPrefix);
const log = getLogger("checkForAndPlaceOrder:_pollLegacy", orderRef);
const { handler } = conditionalOrder.params;
// as we going to use multicall, with `aggregate3Value`, there is no need to do any simulation as the
// calls are guaranteed to pass, and will return the results, or the reversion within the ABI-encoded data.
Expand Down Expand Up @@ -589,7 +602,7 @@ async function _pollLegacy(
} catch (error: any) {
// We can only get here from some provider / ethers failure. As the contract hasn't had it's say
// we will defer to try again.
log.error(`${logPrefix} ethers/call Unexpected error`, error);
log.error(`ethers/call Unexpected error`, error);
pollingOnChainEthersErrorsTotal.labels(...metricLabels).inc();
return {
result: PollResultCode.TRY_NEXT_BLOCK,
Expand Down
18 changes: 9 additions & 9 deletions src/utils/api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Server } from "http";
import express, { Request, Response, Router } from "express";
import { Express } from "express-serve-static-core";
import promMiddleware from "express-prometheus-middleware";
import * as client from "prom-client";
import { getLogger } from "./logging";
import { DBService } from "./db";
import { Registry } from "../types";
Expand All @@ -23,17 +23,17 @@ export class ApiService {

private bootstrap() {
this.app.use(express.json());
this.app.use(
promMiddleware({
metricsPath: "/metrics",
metricsApp: this.app,
collectDefaultMetrics: true,
})
);

client.collectDefaultMetrics();
this.app.use(express.urlencoded({ extended: true }));
this.app.get("/", (_req: Request, res: Response) => {
res.send("🐮 Moooo!");
});
this.app.use("/metrics", (_req: Request, res: Response) => {
const { register } = client;
res.setHeader("Content-Type", register.contentType);
register.metrics().then((data) => res.status(200).send(data));
});
this.app.get("/health", async (_req: Request, res: Response) => {
const health = ChainContext.health;
res.status(health.overallHealth ? 200 : 503).send(health);
Expand All @@ -51,7 +51,7 @@ export class ApiService {
async start(): Promise<Server> {
return await new Promise((resolve, reject) => {
try {
const log = getLogger("api");
const log = getLogger("api:start");
if (this.server?.listening) {
throw new Error("Server is already running");
}
Expand Down
11 changes: 6 additions & 5 deletions src/utils/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,15 +200,17 @@ export function handleOnChainCustomError(params: {
revertData,
metricLabels,
} = params;
const logPrefix = `contracts:handleOnChainCustomError:${orderRef}`;

try {
// The below will throw if:
// - the error is not a custom error (ie. the selector is not in the map)
// - the error is a custom error, but the parameters are not as expected
const parsedCustomError = parseCustomError(revertData);
const { selector } = parsedCustomError;
const log = getLogger(`${logPrefix}:${selector}`);
const log = getLogger(
"contracts:handleOnChainCustomError",
orderRef,
selector
);
const msgWithSelector = (message: string): string =>
`${selector}: ${message}`;
const dropOrder = (reason: string): PollResultErrors => {
Expand Down Expand Up @@ -279,8 +281,7 @@ export function handleOnChainCustomError(params: {
} catch (err: any) {
// Any errors thrown here can _ONLY_ come from non-compliant interfaces (ie. bad revert ABI encoding).
// We log the error, and return a DONT_TRY_AGAIN result.
// TODO: Add metrics to track this
const log = getLogger(logPrefix);
const log = getLogger("contracts:handleOnChainCustomError", orderRef);
log.debug(
`Non-compliant interface error thrown${
err.message ? `: ${err.message}` : ""
Expand Down
30 changes: 27 additions & 3 deletions src/utils/logging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {
setLevel,
getLogger as getLoggerLogLevel,
LogLevelNames,
Logger,
} from "loglevel";
import rootLogger from "loglevel";
import prefix from "loglevel-plugin-prefix";
Expand Down Expand Up @@ -47,7 +46,18 @@ export function initLogging({ logLevel }: { logLevel?: string }) {
});
}

export function getLogger(loggerName: string): Logger {
export interface LoggerWithMethods {
trace: (...msg: any[]) => void;
debug: (...msg: any[]) => void;
info: (...msg: any[]) => void;
warn: (...msg: any[]) => void;
error: (...msg: any[]) => void;
}

export function getLogger(
loggerName: string,
...args: string[]
): LoggerWithMethods {
if (!logLevelOverrides) {
throw new Error("Logging hasn't been initialized");
}
Expand All @@ -60,7 +70,21 @@ export function getLogger(loggerName: string): Logger {
if (logLevelOverride) {
logger.setLevel(logLevelOverride.level);
}
return logger;

const fmtLogMessage = (args: string[], ...msg: any[]) => {
if (args.length) {
return [`${args.join(":")}: `, ...msg];
}
return msg;
};
const customLogger: LoggerWithMethods = {
trace: (...msg: any[]) => logger.trace(...fmtLogMessage(args, ...msg)),
debug: (...msg: any[]) => logger.debug(...fmtLogMessage(args, ...msg)),
info: (...msg: any[]) => logger.info(...fmtLogMessage(args, ...msg)),
warn: (...msg: any[]) => logger.warn(...fmtLogMessage(args, ...msg)),
error: (...msg: any[]) => logger.error(...fmtLogMessage(args, ...msg)),
};
return customLogger;
}

function setRootLogLevel(level: string) {
Expand Down
2 changes: 1 addition & 1 deletion src/utils/poll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export async function pollConditionalOrder(
conditionalOrderParams: ConditionalOrderParams,
orderRef: string
): Promise<PollResult | undefined> {
const log = getLogger(`pollConditionalOrder:${orderRef}`);
const log = getLogger("pollConditionalOrder:pollConditionalOrder", orderRef);
const order = ordersFactory.fromParams(conditionalOrderParams);

if (!order) {
Expand Down
Loading
Loading