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 cache deserialisation of BigNumber #38

Merged
merged 1 commit into from
Oct 1, 2024
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
61 changes: 48 additions & 13 deletions src/api/v2/queries/history.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import type { Request, Response } from "express";
import { CACHE_TTL_ANCHORED, CACHE_TTL_PENDING, DPID_ENV, getCeramicClient } from "../../../util/config.js";
import { type CeramicClient } from "@desci-labs/desci-codex-lib";
import parentLogger from "../../../logger.js";
import { resolveDpid } from "../resolvers/dpid.js";
import parentLogger, { serializeError } from "../../../logger.js";
import { DpidResolverError, resolveDpid } from "../resolvers/dpid.js";
import { isDpid } from "../../../util/validation.js";
import { CommitID, StreamID } from "@desci-labs/desci-codex-lib/dist/streams.js";
import { getFromCache, keyBump, setToCache } from "../../../redis.js";
import { cleanupEip155Address } from "../../../util/conversions.js";

const MODULE_PATH = "api/v2/queries/history" as const;
const logger = parentLogger.child({
module: "api/v2/queries/history",
module: MODULE_PATH,
});

export type HistoryQueryRequest = {
Expand All @@ -22,7 +23,15 @@ export type HistoryQueryParams = {
id?: string;
};

export type HistoryQueryResponse = HistoryQueryResult[] | HistoryQueryError;
export type ErrorResponse = {
error: string;
details: unknown;
body: unknown;
params: unknown;
path: typeof MODULE_PATH;
};

export type HistoryQueryResponse = HistoryQueryResult[] | ErrorResponse;

export type HistoryVersion = {
/** Manifest CID at this version */
Expand All @@ -44,8 +53,6 @@ export type HistoryQueryResult = {
versions: HistoryVersion[];
};

export type HistoryQueryError = string;

/**
* For one or more IDs, fetch metadata and version history.
* An ID can be both a streamID and a dPID, but a dPID lookup is a bit slower.
Expand All @@ -57,10 +64,20 @@ export const historyQueryHandler = async (
const { id } = req.params;
const { ids = [] } = req.body;

const baseError = {
params: req.params,
body: req.body,
path: MODULE_PATH,
};

if (!Array.isArray(ids)) {
// Received ids in body, but not as array
logger.error({ body: req.body, params: req.params }, "received malformed IDs");
return res.status(400).send("body.ids expects string[]");
logger.error(baseError, "received malformed IDs");
return res.status(400).send({
error: "invalid request",
details: "body.ids expects string[]",
...baseError,
});
}

if (id) {
Expand All @@ -70,8 +87,12 @@ export const historyQueryHandler = async (

if (ids.length === 0) {
// Neither ID format was supplied
logger.error({ body: req.body, params: req.params }, "request missing IDs");
return res.status(400).send("missing /:id or ids array in body");
logger.error(baseError, "request missing IDs");
return res.status(400).send({
error: "invalid request",
details: "missing /:id or ids array in body",
...baseError,
});
}

logger.info({ ids }, "handling history query");
Expand All @@ -87,9 +108,23 @@ export const historyQueryHandler = async (
]);
const result = [...codexHistories, ...dpidHistories];
return res.send(result);
} catch (error) {
logger.error({ ids, error }, "failed to compile histories");
return res.status(500).send("failed to compile histories");
} catch (e) {
if (e instanceof DpidResolverError) {
const errPayload = {
error: "failed to resolve dpid",
details: serializeError(e),
...baseError,
};
logger.error(errPayload, "failed to resolve dpid");
return res.status(500).send(errPayload);
}
const errPayload = {
error: "failed to compile histories",
details: serializeError(e as Error),
...baseError,
};
logger.error(errPayload, "failed to compile histories");
return res.status(500).send(errPayload);
}
};

Expand Down
6 changes: 3 additions & 3 deletions src/api/v2/resolvers/codex.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Request, Response } from "express";
import parentLogger from "../../../logger.js";
import parentLogger, { serializeError } from "../../../logger.js";
import { pidFromStringID, type PID } from "@desci-labs/desci-codex-lib";
import { getCodexHistory, type HistoryQueryResult } from "../queries/history.js";

Expand Down Expand Up @@ -46,7 +46,7 @@ export const resolveCodexHandler = async (
} catch (e) {
const errPayload = {
error: "Invalid stream or commit ID",
details: "Could not coerce ID into neither stream nor commitID",
details: serializeError(e as Error),
params: req.params,
path: MODULE_PATH,
};
Expand All @@ -67,7 +67,7 @@ export const resolveCodexHandler = async (
logger.error({ streamId, versionIx, err }, "failed to resolve stream");
return res.status(404).send({
error: "Could not resolve; does stream/version exist?",
details: err,
details: serializeError(err),
params: req.params,
path: MODULE_PATH,
});
Expand Down
22 changes: 11 additions & 11 deletions src/api/v2/resolvers/dpid.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type { Request, Response } from "express";
import parentLogger from "../../../logger.js";
import parentLogger, { serializeError } from "../../../logger.js";
import { CACHE_TTL_ANCHORED, CACHE_TTL_PENDING, DPID_ENV, getDpidAliasRegistry } from "../../../util/config.js";
import { ResolverError } from "../../../errors.js";
import { getCodexHistory, type HistoryQueryResult, type HistoryVersion } from "../queries/history.js";
import { getFromCache, setToCache } from "../../../redis.js";
import type { DpidAliasRegistry } from "@desci-labs/desci-contracts/dist/typechain-types/DpidAliasRegistry.js";
import { BigNumber } from "ethers";

const MODULE_PATH = "/api/v2/resolvers/codex" as const;
const MODULE_PATH = "/api/v2/resolvers/dpid" as const;
const logger = parentLogger.child({
module: MODULE_PATH,
});
Expand Down Expand Up @@ -53,7 +53,7 @@ export const resolveDpidHandler = async (
if (e instanceof DpidResolverError) {
const errPayload = {
error: e.message,
details: e.cause,
details: serializeError(e.cause),
params: req.params,
path: MODULE_PATH,
};
Expand All @@ -63,7 +63,7 @@ export const resolveDpidHandler = async (
const err = e as Error;
const errPayload = {
error: err.message,
details: err,
details: serializeError(err),
params: req.params,
path: MODULE_PATH,
};
Expand Down Expand Up @@ -148,8 +148,11 @@ export const resolveDpid = async (dpid: number, versionIx?: number): Promise<His
setToCache(streamCacheKey, "", CACHE_TTL_PENDING);
}
} else {
// The BigNumbers are parsed into objects, which ethers.BigNumber in fine with
resolvedEntry = JSON.parse(fromCache);
// The BigNumbers are deserialized into objects, which ethers.BigNumber can instantiate from
resolvedEntry[1].forEach((v) => {
v[1] = BigNumber.from(v[1]);
});
Comment on lines +152 to +155
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the meat: when deserialising from cache, instantiate as an actual BigNumber object

}

const owner = resolvedEntry[0];
Expand All @@ -166,11 +169,11 @@ export const resolveDpid = async (dpid: number, versionIx?: number): Promise<His
version: "",
// When restored from redis, the BigNumber is deserialised as a regular object
// Ethers can instantiate the class from that format
time: BigNumber.from(time).toNumber(),
time: time.toNumber(),
manifest,
})),
};
logger.info(result, "manifest resolved via fallback to legacy entry");
logger.info({ dpid, owner, manifest: result.manifest }, "manifest resolved via fallback to legacy entry");
return result;
} catch (e) {
throw new DpidResolverError({
Expand Down Expand Up @@ -199,10 +202,7 @@ const undupeIfLegacyDevHistory = (versions: LegacyVersion[]) => {
}, [] as LegacyVersion[]);
};

const isLegacyDupe = (
[aCid, aTimeBn]: LegacyVersion,
[bCid, bTimeBn]: LegacyVersion
): Boolean => {
const isLegacyDupe = ([aCid, aTimeBn]: LegacyVersion, [bCid, bTimeBn]: LegacyVersion): boolean => {
const cidIsEqual = aCid === bCid;
const timeIsEqual = aTimeBn.toNumber() === bTimeBn.toNumber();
return cidIsEqual && timeIsEqual;
Expand Down
8 changes: 4 additions & 4 deletions src/api/v2/resolvers/generic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Request, Response } from "express";
import axios from "axios";
import type { ResearchObjectV1 } from "@desci-labs/desci-models";

import parentLogger from "../../../logger.js";
import parentLogger, { serializeError } from "../../../logger.js";
import analytics, { LogEventType } from "../../../analytics.js";
import { IPFS_GATEWAY, getNodesUrl } from "../../../util/config.js";
import { DpidResolverError, resolveDpid } from "./dpid.js";
Expand Down Expand Up @@ -152,7 +152,7 @@ export const resolveGenericHandler = async (
if (e instanceof DpidResolverError) {
const errPayload = {
error: e.message,
details: e.cause,
details: serializeError(e.cause),
...baseError,
};
logger.error(errPayload, "failed to resolve dpid");
Expand All @@ -161,7 +161,7 @@ export const resolveGenericHandler = async (
const err = e as Error;
const errPayload = {
error: err.message,
details: err,
details: serializeError(err),
...baseError,
};
logger.error(errPayload, "unexpected error occurred");
Expand Down Expand Up @@ -216,7 +216,7 @@ export const resolveGenericHandler = async (
// Doesn't seem it was a validDagUrl
const errPayload = {
error: "Failed to resolve DAG URL; check path and versioning",
details: e,
details: serializeError(e as Error),
...baseError,
};
logger.error(errPayload, "got invalid DAG URL");
Expand Down
2 changes: 2 additions & 0 deletions src/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ function omitBuffer(array: any) {
});
}

export const serializeError = (e: Error) => pino.stdSerializers.err(e);

process.on("uncaughtException", (err) => {
logger.fatal(err, "uncaught exception");
});