Skip to content

Commit

Permalink
Merge pull request #38 from desci-labs/m0ar/hotfix-bignumber-cache-de…
Browse files Browse the repository at this point in the history
…serialisation

Fix cache deserialisation of `BigNumber`
  • Loading branch information
m0ar authored Oct 1, 2024
2 parents 782321e + f4374ba commit bec3927
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 31 deletions.
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]);
});
}

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");
});

0 comments on commit bec3927

Please sign in to comment.