From 4b625b3790c674c4ebbaaf764452417940d13a69 Mon Sep 17 00:00:00 2001 From: jsy1218 <91580504+jsy1218@users.noreply.github.com> Date: Mon, 1 Jul 2024 14:45:53 -0700 Subject: [PATCH] feat: implement quicknode geth vs reth output comparison --- lib/rpc/SingleJsonRpcProvider.ts | 18 +++++++++++++- lib/rpc/UniJsonRpcProvider.ts | 40 ++++++++++++++++++++++++++++---- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/lib/rpc/SingleJsonRpcProvider.ts b/lib/rpc/SingleJsonRpcProvider.ts index c208fe5d25..e2787eeeff 100644 --- a/lib/rpc/SingleJsonRpcProvider.ts +++ b/lib/rpc/SingleJsonRpcProvider.ts @@ -180,7 +180,7 @@ export class SingleJsonRpcProvider extends StaticJsonRpcProvider { this.logEvaluateLatency() this.evaluatingLatency = true try { - await (this as any)[`${methodName}_EvaluateLatency`](...args) + return await (this as any)[`${methodName}_EvaluateLatency`](...args) } catch (error: any) { this.log.error({ error }, `Encounter error for shadow evaluate latency call: ${JSON.stringify(error)}`) // Swallow the error. @@ -241,6 +241,22 @@ export class SingleJsonRpcProvider extends StaticJsonRpcProvider { metric.putMetric(`${this.metricPrefix}_send_${method}`, 1, MetricLoggerUnit.Count) } + logRpcResponseMatch(method: string, otherProvider: SingleJsonRpcProvider) { + metric.putMetric( + `${this.metricPrefix}_other_provider_${otherProvider.providerId}_method_${method}_rpc_match`, + 1, + MetricLoggerUnit.Count + ) + } + + logRpcResponseMismatch(method: string, otherProvider: SingleJsonRpcProvider) { + metric.putMetric( + `${this.metricPrefix}_other_provider_${otherProvider.providerId}_method_${method}_rpc_mismatch`, + 1, + MetricLoggerUnit.Count + ) + } + private async wrappedFunctionCall( callType: CallType, fnName: string, diff --git a/lib/rpc/UniJsonRpcProvider.ts b/lib/rpc/UniJsonRpcProvider.ts index 03b36b6804..73cea1dd63 100644 --- a/lib/rpc/UniJsonRpcProvider.ts +++ b/lib/rpc/UniJsonRpcProvider.ts @@ -214,7 +214,8 @@ export class UniJsonRpcProvider extends StaticJsonRpcProvider { latency: number, selectedProvider: SingleJsonRpcProvider, methodName: string, - args: any[] + args: any[], + providerResponse: any ): Promise { const healthyProviders = this.providers.filter((provider) => provider.isHealthy()) let count = 0 @@ -230,7 +231,16 @@ export class UniJsonRpcProvider extends StaticJsonRpcProvider { // Within each provider latency shadow evaluation, we should do block I/O, // because NodeJS runs in single thread, so it's important to make sure // we benchmark the latencies correctly based on the single-threaded sequential evaluation. - await provider.evaluateLatency(methodName, args) + const evaluatedProviderResponse = await provider.evaluateLatency(methodName, args) + this.compareRpcResponses( + providerResponse, + evaluatedProviderResponse, + selectedProvider, + provider, + methodName, + args + ) + count++ }) ) @@ -242,6 +252,27 @@ export class UniJsonRpcProvider extends StaticJsonRpcProvider { this.log.debug(`Evaluated ${count} other healthy providers`) } + compareRpcResponses( + providerResponse: any, + evaluatedProviderResponse: any, + selectedProvider: SingleJsonRpcProvider, + otherProvider: SingleJsonRpcProvider, + methodName: string, + args: any[] + ) { + if (providerResponse !== evaluatedProviderResponse) { + this.log.error( + { methodName, args }, + `Provider response mismatch: ${JSON.stringify(providerResponse)} from ${ + selectedProvider.providerId + } vs ${JSON.stringify(evaluatedProviderResponse)} from ${otherProvider.providerId}` + ) + selectedProvider.logRpcResponseMismatch(methodName, otherProvider) + } else { + selectedProvider.logRpcResponseMatch(methodName, otherProvider) + } + } + logProviderHealthiness() { for (const provider of this.providers.filter((provider) => provider.isHealthy())) { this.log.debug(`Healthy provider: ${provider.url}`) @@ -289,9 +320,10 @@ export class UniJsonRpcProvider extends StaticJsonRpcProvider { const selectedProvider = this.selectPreferredProvider(sessionId) selectedProvider.logProviderSelection() let latency = 0 + let result try { const start = Date.now() - const result = await (selectedProvider as any)[`${fnName}`](...args) + result = await (selectedProvider as any)[`${fnName}`](...args) latency = Date.now() - start return result } catch (error: any) { @@ -308,7 +340,7 @@ export class UniJsonRpcProvider extends StaticJsonRpcProvider { sessionId ) { // fire and forget to evaluate latency of other healthy providers - this.checkOtherHealthyProvider(latency, selectedProvider, fnName, args) + this.checkOtherHealthyProvider(latency, selectedProvider, fnName, args, result) } if (Math.random() < this.healthCheckSampleProb && sessionId) {