mirror of
https://github.com/cerc-io/watcher-ts
synced 2025-02-08 11:02:52 +00:00
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:
parent
87224a4673
commit
00a9c247f5
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user