Skip to content

Commit

Permalink
fix: demote server connection errors and contract clarity errors to b…
Browse files Browse the repository at this point in the history
…e non-retryable (#249)
  • Loading branch information
rafaelcr authored Aug 27, 2024
1 parent c220f4e commit 87ad8af
Show file tree
Hide file tree
Showing 7 changed files with 18 additions and 34 deletions.
2 changes: 1 addition & 1 deletion src/token-processor/images/image-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export async function processImageCache(
throw new ImageSizeExceededError(`ImageCache image too large: ${imgUrl}`);
}
if ((typeError.cause as any).toString().includes('ECONNRESET')) {
throw new RetryableJobError(`ImageCache server connection interrupted`, typeError);
throw new ImageHttpError(`ImageCache server connection interrupted`, typeError);
}
}
throw error;
Expand Down
4 changes: 2 additions & 2 deletions src/token-processor/queue/job/process-token-job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
DbTokenType,
} from '../../../pg/types';
import { StacksNodeRpcClient } from '../../stacks-node/stacks-node-rpc-client';
import { StacksNodeClarityError, TooManyRequestsHttpError } from '../../util/errors';
import { SmartContractClarityError, TooManyRequestsHttpError } from '../../util/errors';
import {
fetchAllMetadataLocalesFromBaseUri,
getFetchableDecentralizedStorageUrl,
Expand Down Expand Up @@ -108,7 +108,7 @@ export class ProcessTokenJob extends Job {
} catch (error) {
// We'll treat Clarity errors here as if the supply was `undefined` to accommodate ALEX's
// wrapped tokens which return an error in `get-total-supply`.
if (!(error instanceof StacksNodeClarityError)) {
if (!(error instanceof SmartContractClarityError)) {
throw error;
}
}
Expand Down
19 changes: 7 additions & 12 deletions src/token-processor/stacks-node/stacks-node-rpc-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { request, errors } from 'undici';
import { ENV } from '../../env';
import { RetryableJobError } from '../queue/errors';
import {
StacksNodeClarityError,
SmartContractClarityError,
StacksNodeJsonParseError,
StacksNodeHttpError,
} from '../util/errors';
Expand Down Expand Up @@ -72,7 +72,7 @@ export class StacksNodeRpcClient {
try {
return BigInt(uintVal.value.toString());
} catch (error) {
throw new RetryableJobError(`Invalid uint value '${uintVal.value}'`);
throw new SmartContractClarityError(`Invalid uint value '${uintVal.value}'`);
}
}

Expand Down Expand Up @@ -131,23 +131,18 @@ export class StacksNodeRpcClient {
functionName: string,
functionArgs: ClarityValue[]
): Promise<ClarityValue> {
let result: ReadOnlyContractCallResponse;
try {
result = await this.sendReadOnlyContractCall(functionName, functionArgs);
} catch (error) {
throw new RetryableJobError(`Error making read-only contract call: ${error}`, error);
}
const result = await this.sendReadOnlyContractCall(functionName, functionArgs);
if (!result.okay) {
if (result.cause.startsWith('Runtime')) {
throw new RetryableJobError(
`Runtime error while calling read-only function ${functionName}`
);
} else if (result.cause.includes('NoSuchContract')) {
throw new RetryableJobError(
throw new SmartContractClarityError(
`Contract not available yet when calling read-only function ${functionName}`
);
}
throw new StacksNodeClarityError(`Read-only error ${functionName}: ${result.cause}`);
throw new SmartContractClarityError(`Read-only error ${functionName}: ${result.cause}`);
}
return decodeClarityValue(result.result);
}
Expand All @@ -168,7 +163,7 @@ export class StacksNodeRpcClient {
if (unwrappedClarityValue.type_id === ClarityTypeID.UInt) {
return unwrappedClarityValue;
}
throw new StacksNodeClarityError(
throw new SmartContractClarityError(
`Unexpected Clarity type '${unwrappedClarityValue.type_id}' while unwrapping uint`
);
}
Expand All @@ -183,7 +178,7 @@ export class StacksNodeRpcClient {
} else if (unwrappedClarityValue.type_id === ClarityTypeID.OptionalNone) {
return undefined;
}
throw new StacksNodeClarityError(
throw new SmartContractClarityError(
`Unexpected Clarity type '${unwrappedClarityValue.type_id}' while unwrapping string`
);
}
Expand Down
4 changes: 2 additions & 2 deletions src/token-processor/util/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export class MetadataParseError extends UserError {

export class ImageParseError extends MetadataParseError {}

export class StacksNodeClarityError extends UserError {
export class SmartContractClarityError extends UserError {
constructor(message: string) {
super();
this.message = message;
Expand Down Expand Up @@ -111,7 +111,7 @@ export function getUserErrorInvalidReason(error: UserError): DbJobInvalidReason
return DbJobInvalidReason.metadataHttpError;
case error instanceof ImageHttpError:
return DbJobInvalidReason.imageHttpError;
case error instanceof StacksNodeClarityError:
case error instanceof SmartContractClarityError:
return DbJobInvalidReason.tokenContractClarityError;
default:
return DbJobInvalidReason.unknown;
Expand Down
10 changes: 1 addition & 9 deletions src/token-processor/util/metadata-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,6 @@ export async function fetchAllMetadataLocalesFromBaseUri(
break;
} catch (error) {
fetchImmediateRetryCount++;
if (
error instanceof MetadataTimeoutError &&
isUriFromDecentralizedStorage(error.url.toString())
) {
// Gateways like IPFS and Arweave commonly time out when a resource can't be found quickly.
// Try again later if this is the case.
throw new RetryableJobError(`Gateway timeout for ${error.url}`, error);
}
if (error instanceof TooManyRequestsHttpError) {
// 429 status codes are common when fetching metadata for thousands of tokens in the same
// server.
Expand Down Expand Up @@ -268,7 +260,7 @@ export async function fetchMetadata(httpUrl: URL): Promise<string | undefined> {
error instanceof TypeError &&
((error as UndiciCauseTypeError).cause as any).toString().includes('ECONNRESET')
) {
throw new RetryableJobError(`Server connection interrupted`, error);
throw new MetadataHttpError(`Server connection interrupted`, error);
}
throw new MetadataHttpError(`${url}: ${error}`, error);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/token-queue/metadata-helpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,6 @@ describe('Metadata Helpers', () => {
.replyWithError(Object.assign(new TypeError(), { cause: new Error('read ECONNRESET') }));
setGlobalDispatcher(agent);

await expect(fetchMetadata(url)).rejects.toThrow(RetryableJobError);
await expect(fetchMetadata(url)).rejects.toThrow(MetadataHttpError);
});
});
11 changes: 4 additions & 7 deletions tests/token-queue/stacks-node-rpc-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { StacksNodeRpcClient } from '../../src/token-processor/stacks-node/stack
import {
StacksNodeJsonParseError,
StacksNodeHttpError,
SmartContractClarityError,
} from '../../src/token-processor/util/errors';

describe('StacksNodeRpcClient', () => {
Expand Down Expand Up @@ -69,7 +70,7 @@ describe('StacksNodeRpcClient', () => {
setGlobalDispatcher(agent);

await expect(client.readStringFromContract('get-token-uri', [])).rejects.toThrow(
RetryableJobError
SmartContractClarityError
);
});

Expand Down Expand Up @@ -110,9 +111,7 @@ describe('StacksNodeRpcClient', () => {
try {
await client.readStringFromContract('get-token-uri', []);
} catch (error) {
expect(error).toBeInstanceOf(RetryableJobError);
const err = error as RetryableJobError;
expect(err.cause).toBeInstanceOf(StacksNodeHttpError);
expect(error).toBeInstanceOf(StacksNodeHttpError);
}
});

Expand All @@ -131,9 +130,7 @@ describe('StacksNodeRpcClient', () => {
try {
await client.readStringFromContract('get-token-uri', []);
} catch (error) {
expect(error).toBeInstanceOf(RetryableJobError);
const err = error as RetryableJobError;
expect(err.cause).toBeInstanceOf(StacksNodeJsonParseError);
expect(error).toBeInstanceOf(StacksNodeJsonParseError);
}
});

Expand Down

0 comments on commit 87ad8af

Please sign in to comment.