From c40d7d98c5e842638e919be3a4b43fe4c03e3925 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Mon, 18 Sep 2023 20:46:39 -0600 Subject: [PATCH 1/3] fix: use updated promise cache that does not await on puts that result in uncaught exceptions --- package.json | 2 +- src/api/warp.ts | 71 +++++++++++++------------------------------------ src/app.ts | 2 +- yarn.lock | 8 +++--- 4 files changed, 24 insertions(+), 59 deletions(-) diff --git a/package.json b/package.json index 1708ab1..64d0c0d 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "prepare": "husky install" }, "dependencies": { - "@ardrive/ardrive-promise-cache": "^1.1.3", + "@ardrive/ardrive-promise-cache": "^1.1.4-alpha.1", "@commitlint/config-conventional": "^17.7.0", "@koa/cors": "^4.0.0", "@koa/router": "^12.0.0", diff --git a/src/api/warp.ts b/src/api/warp.ts index 346b467..b33ae80 100644 --- a/src/api/warp.ts +++ b/src/api/warp.ts @@ -4,11 +4,7 @@ import { EvaluationOptions, Warp, } from 'warp-contracts'; -import { - DEFAULT_EVALUATION_OPTIONS, - EVALUATION_TIMEOUT_MS, - allowedContractTypes, -} from '../constants'; +import { EVALUATION_TIMEOUT_MS, allowedContractTypes } from '../constants'; import { ContractType } from '../types'; import * as _ from 'lodash'; import { EvaluationTimeoutError } from '../errors'; @@ -61,7 +57,7 @@ const contractStateCache: ReadThroughPromiseCache< cacheCapacity: 100, cacheTTL: 1000 * 30, // 30 seconds }, - readThroughFunction: (cacheKey) => readThroughToContractState(cacheKey), + readThroughFunction: readThroughToContractState, }); // Convenience class for read through caching @@ -87,7 +83,7 @@ const contractManifestCache: ReadThroughPromiseCache< cacheCapacity: 1000, cacheTTL: 1000 * 60 * 60 * 24 * 365, // 365 days - effectively permanent }, - readThroughFunction: (cacheKey) => readThroughToContractManifest(cacheKey), + readThroughFunction: readThroughToContractManifest, }); function createQueryParamHash(evalOptions: Partial): string { @@ -120,13 +116,13 @@ async function readThroughToContractState( ...cachedValue, evaluationOptions, }; - } else { - logger?.debug('Evaluating contract state...', { - contractTxId, - cacheKey: cacheKey.toString(), - }); } + logger?.debug('Evaluating contract state...', { + contractTxId, + cacheKey: cacheKey.toString(), + }); + // use the combined evaluation options const contract = warp .contract(contractTxId) @@ -137,14 +133,17 @@ async function readThroughToContractState( requestMap.set(cacheId, readStatePromise); readStatePromise - .catch((error) => { + .catch((error: unknown) => { logger?.debug('Failed to evaluate contract state!', { contractTxId, cacheKey: cacheKey.toString(), - error, + error: error instanceof Error ? error.message : 'Unknown error', }); }) .finally(() => { + logger?.debug('Removing cached rqeuest from barrier map.', { + cacheId, + }); // remove the cached request whether it completes or fails requestMap.delete(cacheId); }); @@ -166,36 +165,20 @@ async function readThroughToContractState( export async function getContractState({ contractTxId, warp, - evaluationOptionOverrides = DEFAULT_EVALUATION_OPTIONS, logger, }: { contractTxId: string; warp: Warp; - evaluationOptionOverrides?: Partial; logger: winston.Logger; }): Promise { try { // get the contract manifest eval options by default - const { evaluationOptions: contractDefinedEvalOptions } = - await getContractManifest({ - contractTxId, - arweave: warp.arweave, - logger, - }); - // override any contract manifest eval options with eval options provided - const combinedEvalOptions = { - ...contractDefinedEvalOptions, - ...evaluationOptionOverrides, - }; - + const { evaluationOptions } = await contractManifestCache.get( + new ContractManifestCacheKey(contractTxId, warp.arweave, logger), + ); // Awaiting here so that promise rejection can be caught below, wrapped, and propagated return await contractStateCache.get( - new ContractStateCacheKey( - contractTxId, - combinedEvalOptions, - warp, - logger, - ), + new ContractStateCacheKey(contractTxId, evaluationOptions, warp, logger), ); } catch (error) { // throw an eval here so we can properly return correct status code @@ -215,11 +198,7 @@ async function readThroughToContractManifest({ contractTxId, arweave, logger, -}: { - contractTxId: string; - arweave: Arweave; - logger?: winston.Logger; -}): Promise { +}: ContractManifestCacheKey): Promise { logger?.debug('Reading through to contract manifest...', { contractTxId, }); @@ -231,20 +210,6 @@ async function readThroughToContractManifest({ return contractManifest; } -export async function getContractManifest({ - contractTxId, - arweave, - logger, -}: { - contractTxId: string; - arweave: Arweave; - logger?: winston.Logger; -}): Promise { - return contractManifestCache.get( - new ContractManifestCacheKey(contractTxId, arweave, logger), - ); -} - export function tagsToObject(tags: Tag[]): { [x: string]: string; } { diff --git a/src/app.ts b/src/app.ts index f05d2aa..2613694 100644 --- a/src/app.ts +++ b/src/app.ts @@ -30,7 +30,7 @@ const errorCounter = new promClient.Counter({ // catch any floating errors, swallow them and increment prometheus counter process.on('uncaughtException', (err) => { - logger.error(err); + logger.error('Uncaught exception!', err); errorCounter.inc(); }); diff --git a/yarn.lock b/yarn.lock index c1ff476..fbb2c60 100644 --- a/yarn.lock +++ b/yarn.lock @@ -117,10 +117,10 @@ dependencies: xss "^1.0.8" -"@ardrive/ardrive-promise-cache@^1.1.3": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@ardrive/ardrive-promise-cache/-/ardrive-promise-cache-1.1.3.tgz#93dc04f54bcccfc90b97d9aeb1095bb6374eacc8" - integrity sha512-jPFlnVUlHcW+LEptyUyXl2oZ6JZeuWTycZ/tfOWxXL9go+0icUHUWibIvJAuqeR4EjBFsT953BOibplPsZ/b1w== +"@ardrive/ardrive-promise-cache@^1.1.4-alpha.1": + version "1.1.4-alpha.1" + resolved "https://registry.yarnpkg.com/@ardrive/ardrive-promise-cache/-/ardrive-promise-cache-1.1.4-alpha.1.tgz#a35507eff7f4ecd13c0a692f2e6d8f7450fd344f" + integrity sha512-XiX230ReWAsVc9Nr9e65qdRgip4TmlOQhKFozrX3FUmh+Ee5ry0Q4B5XMmn/jgGOuAIrGeI8tO+vCW/y90uS6A== dependencies: "@alexsasharegan/simple-cache" "^3.3.3" From 2f6813ae087ecc02d9f8074ce6eb14e848dbb247 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Tue, 19 Sep 2023 11:40:07 -0600 Subject: [PATCH 2/3] chore: typo in log --- src/api/warp.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/warp.ts b/src/api/warp.ts index b33ae80..90ecf4b 100644 --- a/src/api/warp.ts +++ b/src/api/warp.ts @@ -141,7 +141,7 @@ async function readThroughToContractState( }); }) .finally(() => { - logger?.debug('Removing cached rqeuest from barrier map.', { + logger?.debug('Removing cached request from barrier map.', { cacheId, }); // remove the cached request whether it completes or fails From 34a2ea18f147400b33ccfa889bdc859ea52b8ecb Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Tue, 19 Sep 2023 11:47:47 -0600 Subject: [PATCH 3/3] fix: update type for contractManifestCache, tweak logs for in-flight requests, and add default evaluation options --- src/api/warp.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/api/warp.ts b/src/api/warp.ts index 90ecf4b..16037fa 100644 --- a/src/api/warp.ts +++ b/src/api/warp.ts @@ -4,7 +4,11 @@ import { EvaluationOptions, Warp, } from 'warp-contracts'; -import { EVALUATION_TIMEOUT_MS, allowedContractTypes } from '../constants'; +import { + DEFAULT_EVALUATION_OPTIONS, + EVALUATION_TIMEOUT_MS, + allowedContractTypes, +} from '../constants'; import { ContractType } from '../types'; import * as _ from 'lodash'; import { EvaluationTimeoutError } from '../errors'; @@ -77,7 +81,7 @@ class ContractManifestCacheKey { // Aggressively cache contract manifests since they're permanent on chain const contractManifestCache: ReadThroughPromiseCache< ContractManifestCacheKey, - any + EvaluationManifest > = new ReadThroughPromiseCache({ cacheParams: { cacheCapacity: 1000, @@ -141,7 +145,7 @@ async function readThroughToContractState( }); }) .finally(() => { - logger?.debug('Removing cached request from barrier map.', { + logger?.debug('Removing request from in-flight cache.', { cacheId, }); // remove the cached request whether it completes or fails @@ -173,9 +177,10 @@ export async function getContractState({ }): Promise { try { // get the contract manifest eval options by default - const { evaluationOptions } = await contractManifestCache.get( - new ContractManifestCacheKey(contractTxId, warp.arweave, logger), - ); + const { evaluationOptions = DEFAULT_EVALUATION_OPTIONS } = + await contractManifestCache.get( + new ContractManifestCacheKey(contractTxId, warp.arweave, logger), + ); // Awaiting here so that promise rejection can be caught below, wrapped, and propagated return await contractStateCache.get( new ContractStateCacheKey(contractTxId, evaluationOptions, warp, logger),