Compare CLI enhancements for verifying uniswap-watcher entities (#188)

* Update compare CLI to verify only updated entities

* Show time difference between GQL requests
This commit is contained in:
nikugogoi 2022-10-04 11:18:55 +05:30 committed by GitHub
parent 87224a4673
commit 00a9c247f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 107 additions and 52 deletions

View File

@ -58,16 +58,17 @@
* For comparing queries in a range of blocks:
* Config file should have the names of queries to be fired.
* Config file should have the names of queries to be fired along with the corresponding entity names.
```toml
[queries]
queryDir = "../graph-test-watcher/src/gql/queries"
names = [
"author",
"blog"
]
[queries.names]
author = "Author"
blog = "Blog"
```
The queries will be fired if the corresponding entities are updated.
* Run the CLI:
@ -86,10 +87,9 @@
[queries]
queryDir = "../graph-test-watcher/src/gql/queries"
names = [
"author",
"blog"
]
[queries.names]
author = "Author"
blog = "Blog"
[watcher]
configPath = "../../graph-test-watcher/environments/local.toml"

View File

@ -4,9 +4,9 @@
[queries]
queryDir = "../../graph-test-watcher/src/gql/queries"
names = []
blockDelayInMs = 250
queryLimits = {}
[queries.names]
[watcher]
configPath = "../../graph-test-watcher/environments/local.toml"

View File

@ -17,9 +17,11 @@ export class Client {
_queryDir: string;
_cache: Cache | undefined;
_endpoint: string;
_timeDiff: boolean;
constructor (config: Config, queryDir: string) {
constructor (config: Config, timeDiff: boolean, queryDir: string) {
this._config = config;
this._timeDiff = timeDiff;
this._queryDir = path.resolve(process.cwd(), queryDir);
const { gqlEndpoint, cache } = config;
@ -35,8 +37,12 @@ export class Client {
return this._endpoint;
}
async getResult (queryName: string, params: { [key: string]: any }): Promise<any> {
return this._getCachedOrFetch(queryName, params);
async getResult (queryName: string, params: { [key: string]: any }): Promise<{ time: number, data: any }> {
const startTime = new Date();
const data = await this._getCachedOrFetch(queryName, params);
const time = (new Date()).getTime() - startTime.getTime();
return { time, data };
}
async getIds (queryName: string, blockNumber: number): Promise<string[]> {
@ -80,8 +86,9 @@ export class Client {
params
};
// Check if request cached in db, if cache is enabled.
if (this._cache) {
// Check if request cached in db
// If cache is enabled and timeDiff is disabled.
if (this._cache && !this._timeDiff) {
const [value, found] = await this._cache.get(keyObj) || [undefined, false];
if (found) {
return value;

View File

@ -21,7 +21,9 @@ const log = debug('vulcanize:compare-blocks');
export const main = async (): Promise<void> => {
const argv = await yargs.parserConfiguration({
'parse-numbers': false
}).options({
}).env(
'COMPARE'
).options({
configFile: {
alias: 'cf',
type: 'string',
@ -53,13 +55,18 @@ export const main = async (): Promise<void> => {
type: 'boolean',
describe: 'Fetch ids and compare multiple entities',
default: false
},
timeDiff: {
type: 'boolean',
describe: 'Compare time taken between GQL queries',
default: false
}
}).argv;
const { startBlock, endBlock, rawJson, queryDir, fetchIds, configFile } = argv;
const { startBlock, endBlock, rawJson, queryDir, fetchIds, configFile, timeDiff } = argv;
const config: Config = await getConfig(configFile);
const snakeNamingStrategy = new SnakeNamingStrategy();
const clients = await getClients(config, queryDir);
const clients = await getClients(config, timeDiff, queryDir);
const queryNames = config.queries.names;
let diffFound = false;
let blockDelay = wait(0);
@ -88,32 +95,39 @@ export const main = async (): Promise<void> => {
for (let blockNumber = startBlock; blockNumber <= endBlock; blockNumber++) {
const block = { number: blockNumber };
let updatedEntityIds: string[][] = [];
const updatedEntityIds: { [entityName: string]: string[] } = {};
const updatedEntities: Set<string> = new Set();
let ipldStateByBlock = {};
assert(db);
console.time(`time:compare-block-${blockNumber}`);
if (fetchIds) {
// Fetch entity ids updated at block.
console.time(`time:fetch-updated-ids-${blockNumber}`);
const updatedEntityIdPromises = queryNames.map(
queryName => {
assert(db);
return db.getEntityIdsAtBlockNumber(
blockNumber,
snakeNamingStrategy.tableName(queryName, '')
);
}
);
updatedEntityIds = await Promise.all(updatedEntityIdPromises);
for (const entityName of Object.values(queryNames)) {
updatedEntityIds[entityName] = await db.getEntityIdsAtBlockNumber(
blockNumber,
snakeNamingStrategy.tableName(entityName, '')
);
}
console.timeEnd(`time:fetch-updated-ids-${blockNumber}`);
} else {
for (const entityName of Object.values(queryNames)) {
const isUpdated = await db.isEntityUpdatedAtBlockNumber(
blockNumber,
snakeNamingStrategy.tableName(entityName, '')
);
if (isUpdated) {
updatedEntities.add(entityName);
}
}
}
if (config.watcher.verifyState) {
assert(db);
const [block] = await db?.getBlocksAtHeight(blockNumber, false);
const [block] = await db.getBlocksAtHeight(blockNumber, false);
assert(subgraphGQLClient);
const contractIPLDsByBlock = await getIPLDsByBlock(subgraphGQLClient, subgraphContracts, block.blockHash);
@ -130,7 +144,7 @@ export const main = async (): Promise<void> => {
}
await blockDelay;
for (const [index, queryName] of queryNames.entries()) {
for (const [queryName, entityName] of Object.entries(queryNames)) {
try {
log(`At block ${blockNumber} for query ${queryName}:`);
let resultDiff = '';
@ -140,16 +154,17 @@ export const main = async (): Promise<void> => {
if (queryLimit) {
// Take only last `queryLimit` entity ids to compare in GQL.
const idsLength = updatedEntityIds[index].length;
updatedEntityIds[index].splice(0, idsLength - queryLimit);
const idsLength = updatedEntityIds[entityName].length;
updatedEntityIds[entityName].splice(0, idsLength - queryLimit);
}
for (const id of updatedEntityIds[index]) {
for (const id of updatedEntityIds[entityName]) {
const { diff, result1: result } = await compareQuery(
clients,
queryName,
{ block, id },
rawJson
rawJson,
timeDiff
);
if (config.watcher.verifyState) {
@ -166,12 +181,15 @@ export const main = async (): Promise<void> => {
}
}
} else {
({ diff: resultDiff } = await compareQuery(
clients,
queryName,
{ block },
rawJson
));
if (updatedEntities.has(entityName)) {
({ diff: resultDiff } = await compareQuery(
clients,
queryName,
{ block },
rawJson,
timeDiff
));
}
}
if (resultDiff) {

View File

@ -13,7 +13,9 @@ const log = debug('vulcanize:compare-entity');
export const main = async (): Promise<void> => {
const argv = await yargs.parserConfiguration({
'parse-numbers': false
}).options({
}).env(
'COMPARE'
).options({
configFile: {
alias: 'cf',
type: 'string',
@ -50,6 +52,11 @@ export const main = async (): Promise<void> => {
type: 'boolean',
describe: 'Whether to print out raw diff object',
default: false
},
timeDiff: {
type: 'boolean',
describe: 'Compare time taken between GQL queries',
default: false
}
}).argv;
@ -63,9 +70,9 @@ export const main = async (): Promise<void> => {
hash: argv.blockHash
};
const clients = await getClients(config, argv.queryDir);
const clients = await getClients(config, argv.timeDiff, argv.queryDir);
const { diff } = await compareQuery(clients, queryName, { id, block }, argv.rawJson);
const { diff } = await compareQuery(clients, queryName, { id, block }, argv.rawJson, argv.timeDiff);
if (diff) {
log(diff);

View File

@ -10,6 +10,7 @@ import fs from 'fs-extra';
import { diffString, diff } from 'json-diff';
import _ from 'lodash';
import omitDeep from 'omit-deep';
import debug from 'debug';
import { Config as CacheConfig, getCache } from '@cerc-io/cache';
import { GraphQLClient } from '@cerc-io/ipld-eth-client';
@ -18,6 +19,8 @@ import { gql } from '@apollo/client/core';
import { Client } from './client';
import { DEFAULT_LIMIT } from '../../database';
const log = debug('vulcanize:compare-utils');
const IPLD_STATE_QUERY = `
query getState($blockHash: String!, $contractAddress: String!, $kind: String){
getState(blockHash: $blockHash, contractAddress: $contractAddress, kind: $kind){
@ -42,7 +45,7 @@ interface EndpointConfig {
interface QueryConfig {
queryDir: string;
names: string[];
names: { [queryName: string]: string };
blockDelayInMs: number;
queryLimits: { [queryName: string]: number }
}
@ -100,15 +103,23 @@ export const compareQuery = async (
},
queryName: string,
params: { [key: string]: any },
rawJson: boolean
rawJson: boolean,
timeDiff: boolean
): Promise<CompareResult> => {
const { client1, client2 } = clients;
const [result1, result2] = await Promise.all([
const [
{ data: result1, time: time1 },
{ data: result2, time: time2 }
] = await Promise.all([
client1.getResult(queryName, params),
client2.getResult(queryName, params)
]);
if (timeDiff) {
log(`time:utils#compareQuery-${queryName}-${JSON.stringify(params)}-gql1-[${time1}ms]-gql2-[${time2}ms]-diff-[${time1 - time2}ms]`);
}
// Getting the diff of two result objects.
const resultDiff = compareObjects(result1, result2, rawJson);
@ -119,7 +130,7 @@ export const compareQuery = async (
};
};
export const getClients = async (config: Config, queryDir?: string):Promise<{
export const getClients = async (config: Config, timeDiff: boolean, queryDir?: string):Promise<{
client1: Client,
client2: Client
}> => {
@ -145,12 +156,12 @@ export const getClients = async (config: Config, queryDir?: string):Promise<{
const client1 = new Client({
gqlEndpoint: gqlEndpoint1,
cache: endpoint === 'gqlEndpoint1' ? cache : undefined
}, queryDir);
}, timeDiff, queryDir);
const client2 = new Client({
gqlEndpoint: gqlEndpoint2,
cache: endpoint === 'gqlEndpoint2' ? cache : undefined
}, queryDir);
}, timeDiff, queryDir);
return {
client1,

View File

@ -112,6 +112,18 @@ export class Database {
return entities.map((entity: any) => entity.id);
}
async isEntityUpdatedAtBlockNumber (blockNumber: number, tableName: string): Promise<boolean> {
const repo = this._conn.getRepository(tableName);
const count = await repo.count({
where: {
blockNumber
}
});
return count > 0;
}
async getEntityWithRelations<Entity> (queryRunner: QueryRunner, entity: (new () => Entity), id: string, relationsMap: Map<any, { [key: string]: any }>, block: BlockHeight = {}, depth = 1): Promise<Entity | undefined> {
let { hash: blockHash, number: blockNumber } = block;
const repo = queryRunner.manager.getRepository(entity);