Support fragments in GQL queries for subgraph watchers (#510)

* Avoid updating latest block metrics on RPC errors

* Handle fragments in subgraph GQL queries

* Upgrade package versions

* Move private method in util graph database

---------

Co-authored-by: Prathamesh Musale <prathamesh.musale0@gmail.com>
This commit is contained in:
Nabarun Gogoi 2024-05-17 17:20:29 +05:30 committed by GitHub
parent 1ca74548ff
commit b57aa76d9f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 120 additions and 88 deletions

View File

@ -2,7 +2,7 @@
"packages": [ "packages": [
"packages/*" "packages/*"
], ],
"version": "0.2.88", "version": "0.2.89",
"npmClient": "yarn", "npmClient": "yarn",
"useWorkspaces": true, "useWorkspaces": true,
"command": { "command": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@cerc-io/cache", "name": "@cerc-io/cache",
"version": "0.2.88", "version": "0.2.89",
"description": "Generic object cache", "description": "Generic object cache",
"main": "dist/index.js", "main": "dist/index.js",
"scripts": { "scripts": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@cerc-io/cli", "name": "@cerc-io/cli",
"version": "0.2.88", "version": "0.2.89",
"main": "dist/index.js", "main": "dist/index.js",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"scripts": { "scripts": {
@ -15,13 +15,13 @@
}, },
"dependencies": { "dependencies": {
"@apollo/client": "^3.7.1", "@apollo/client": "^3.7.1",
"@cerc-io/cache": "^0.2.88", "@cerc-io/cache": "^0.2.89",
"@cerc-io/ipld-eth-client": "^0.2.88", "@cerc-io/ipld-eth-client": "^0.2.89",
"@cerc-io/libp2p": "^0.42.2-laconic-0.1.4", "@cerc-io/libp2p": "^0.42.2-laconic-0.1.4",
"@cerc-io/nitro-node": "^0.1.15", "@cerc-io/nitro-node": "^0.1.15",
"@cerc-io/peer": "^0.2.88", "@cerc-io/peer": "^0.2.89",
"@cerc-io/rpc-eth-client": "^0.2.88", "@cerc-io/rpc-eth-client": "^0.2.89",
"@cerc-io/util": "^0.2.88", "@cerc-io/util": "^0.2.89",
"@ethersproject/providers": "^5.4.4", "@ethersproject/providers": "^5.4.4",
"@graphql-tools/utils": "^9.1.1", "@graphql-tools/utils": "^9.1.1",
"@ipld/dag-cbor": "^8.0.0", "@ipld/dag-cbor": "^8.0.0",

View File

@ -8,8 +8,6 @@ import debug from 'debug';
import { ethers } from 'ethers'; import { ethers } from 'ethers';
import JsonRpcProvider = ethers.providers.JsonRpcProvider; import JsonRpcProvider = ethers.providers.JsonRpcProvider;
import { fetchLatestBlockNumber } from '@cerc-io/util';
const log = debug('laconic:chain-head-exporter'); const log = debug('laconic:chain-head-exporter');
// Env overrides: // Env overrides:
@ -57,16 +55,20 @@ async function main (): Promise<void> {
registers: [metricsRegister], registers: [metricsRegister],
labelNames: ['chain'] as const, labelNames: ['chain'] as const,
async collect () { async collect () {
const [ try {
latestEthBlockNumber, const [
latestFilBlockNumber latestEthBlockNumber,
] = await Promise.all([ latestFilBlockNumber
fetchLatestBlockNumber(ethProvider), ] = await Promise.all([
fetchLatestBlockNumber(filProvider) ethProvider.getBlockNumber(),
]); filProvider.getBlockNumber()
]);
this.set({ chain: 'ethereum' }, latestEthBlockNumber); this.set({ chain: 'ethereum' }, latestEthBlockNumber);
this.set({ chain: 'filecoin' }, latestFilBlockNumber); this.set({ chain: 'filecoin' }, latestFilBlockNumber);
} catch (err) {
log('Error fetching latest block number', err);
}
} }
}); });

View File

@ -1,6 +1,6 @@
{ {
"name": "@cerc-io/codegen", "name": "@cerc-io/codegen",
"version": "0.2.88", "version": "0.2.89",
"description": "Code generator", "description": "Code generator",
"private": true, "private": true,
"main": "index.js", "main": "index.js",
@ -20,7 +20,7 @@
}, },
"homepage": "https://github.com/cerc-io/watcher-ts#readme", "homepage": "https://github.com/cerc-io/watcher-ts#readme",
"dependencies": { "dependencies": {
"@cerc-io/util": "^0.2.88", "@cerc-io/util": "^0.2.89",
"@graphql-tools/load-files": "^6.5.2", "@graphql-tools/load-files": "^6.5.2",
"@npmcli/package-json": "^5.0.0", "@npmcli/package-json": "^5.0.0",
"@poanet/solidity-flattener": "https://github.com/vulcanize/solidity-flattener.git", "@poanet/solidity-flattener": "https://github.com/vulcanize/solidity-flattener.git",

View File

@ -10,7 +10,7 @@ import JSONbig from 'json-bigint';
{{/if}} {{/if}}
import { ethers, constants } from 'ethers'; import { ethers, constants } from 'ethers';
{{#if (subgraphPath)}} {{#if (subgraphPath)}}
import { SelectionNode } from 'graphql'; import { GraphQLResolveInfo } from 'graphql';
{{/if}} {{/if}}
import { JsonFragment } from '@ethersproject/abi'; import { JsonFragment } from '@ethersproject/abi';
@ -458,9 +458,9 @@ export class Indexer implements IndexerInterface {
entity: new () => Entity, entity: new () => Entity,
id: string, id: string,
block: BlockHeight, block: BlockHeight,
selections: ReadonlyArray<SelectionNode> = [] queryInfo: GraphQLResolveInfo
): Promise<any> { ): Promise<any> {
const data = await this._graphWatcher.getEntity(entity, id, this._relationsMap, block, selections); const data = await this._graphWatcher.getEntity(entity, id, this._relationsMap, block, queryInfo);
return data; return data;
} }
@ -470,9 +470,9 @@ export class Indexer implements IndexerInterface {
block: BlockHeight, block: BlockHeight,
where: { [key: string]: any } = {}, where: { [key: string]: any } = {},
queryOptions: QueryOptions = {}, queryOptions: QueryOptions = {},
selections: ReadonlyArray<SelectionNode> = [] queryInfo: GraphQLResolveInfo
): Promise<any[]> { ): Promise<any[]> {
return this._graphWatcher.getEntities(entity, this._relationsMap, block, where, queryOptions, selections); return this._graphWatcher.getEntities(entity, this._relationsMap, block, where, queryOptions, queryInfo);
} }
{{/if}} {{/if}}

View File

@ -41,12 +41,12 @@
"homepage": "https://github.com/cerc-io/watcher-ts#readme", "homepage": "https://github.com/cerc-io/watcher-ts#readme",
"dependencies": { "dependencies": {
"@apollo/client": "^3.3.19", "@apollo/client": "^3.3.19",
"@cerc-io/cli": "^0.2.88", "@cerc-io/cli": "^0.2.89",
"@cerc-io/ipld-eth-client": "^0.2.88", "@cerc-io/ipld-eth-client": "^0.2.89",
"@cerc-io/solidity-mapper": "^0.2.88", "@cerc-io/solidity-mapper": "^0.2.89",
"@cerc-io/util": "^0.2.88", "@cerc-io/util": "^0.2.89",
{{#if (subgraphPath)}} {{#if (subgraphPath)}}
"@cerc-io/graph-node": "^0.2.88", "@cerc-io/graph-node": "^0.2.89",
{{/if}} {{/if}}
"@ethersproject/providers": "^5.4.4", "@ethersproject/providers": "^5.4.4",
"debug": "^4.3.1", "debug": "^4.3.1",

View File

@ -106,12 +106,11 @@ export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher
log('{{this.queryName}}', id, JSON.stringify(block, jsonBigIntStringReplacer)); log('{{this.queryName}}', id, JSON.stringify(block, jsonBigIntStringReplacer));
gqlTotalQueryCount.inc(1); gqlTotalQueryCount.inc(1);
gqlQueryCount.labels('{{this.queryName}}').inc(1); gqlQueryCount.labels('{{this.queryName}}').inc(1);
assert(info.fieldNodes[0].selectionSet);
// Set cache-control hints // Set cache-control hints
// setGQLCacheHints(info, block, gqlCacheConfig); // setGQLCacheHints(info, block, gqlCacheConfig);
return indexer.getSubgraphEntity({{this.entityName}}, id, block, info.fieldNodes[0].selectionSet.selections); return indexer.getSubgraphEntity({{this.entityName}}, id, block, info);
}, },
{{this.pluralQueryName}}: async ( {{this.pluralQueryName}}: async (
@ -123,7 +122,6 @@ export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher
log('{{this.pluralQueryName}}', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); log('{{this.pluralQueryName}}', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection);
gqlTotalQueryCount.inc(1); gqlTotalQueryCount.inc(1);
gqlQueryCount.labels('{{this.pluralQueryName}}').inc(1); gqlQueryCount.labels('{{this.pluralQueryName}}').inc(1);
assert(info.fieldNodes[0].selectionSet);
// Set cache-control hints // Set cache-control hints
// setGQLCacheHints(info, block, gqlCacheConfig); // setGQLCacheHints(info, block, gqlCacheConfig);
@ -133,7 +131,7 @@ export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher
block, block,
where, where,
{ limit: first, skip, orderBy, orderDirection }, { limit: first, skip, orderBy, orderDirection },
info.fieldNodes[0].selectionSet.selections info
); );
}, },

View File

@ -1,10 +1,10 @@
{ {
"name": "@cerc-io/graph-node", "name": "@cerc-io/graph-node",
"version": "0.2.88", "version": "0.2.89",
"main": "dist/index.js", "main": "dist/index.js",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"devDependencies": { "devDependencies": {
"@cerc-io/solidity-mapper": "^0.2.88", "@cerc-io/solidity-mapper": "^0.2.89",
"@ethersproject/providers": "^5.4.4", "@ethersproject/providers": "^5.4.4",
"@graphprotocol/graph-ts": "^0.22.0", "@graphprotocol/graph-ts": "^0.22.0",
"@nomiclabs/hardhat-ethers": "^2.0.2", "@nomiclabs/hardhat-ethers": "^2.0.2",
@ -51,9 +51,9 @@
"dependencies": { "dependencies": {
"@apollo/client": "^3.3.19", "@apollo/client": "^3.3.19",
"@cerc-io/assemblyscript": "0.19.10-watcher-ts-0.1.2", "@cerc-io/assemblyscript": "0.19.10-watcher-ts-0.1.2",
"@cerc-io/cache": "^0.2.88", "@cerc-io/cache": "^0.2.89",
"@cerc-io/ipld-eth-client": "^0.2.88", "@cerc-io/ipld-eth-client": "^0.2.89",
"@cerc-io/util": "^0.2.88", "@cerc-io/util": "^0.2.89",
"@types/json-diff": "^0.5.2", "@types/json-diff": "^0.5.2",
"@types/yargs": "^17.0.0", "@types/yargs": "^17.0.0",
"bn.js": "^4.11.9", "bn.js": "^4.11.9",

View File

@ -8,7 +8,7 @@ import debug from 'debug';
import path from 'path'; import path from 'path';
import fs from 'fs'; import fs from 'fs';
import { ContractInterface, utils, providers } from 'ethers'; import { ContractInterface, utils, providers } from 'ethers';
import { SelectionNode } from 'graphql'; import { GraphQLResolveInfo, SelectionNode } from 'graphql';
import { ResultObject } from '@cerc-io/assemblyscript/lib/loader'; import { ResultObject } from '@cerc-io/assemblyscript/lib/loader';
import { import {
@ -321,13 +321,15 @@ export class GraphWatcher {
id: string, id: string,
relationsMap: Map<any, { [key: string]: any }>, relationsMap: Map<any, { [key: string]: any }>,
block: BlockHeight, block: BlockHeight,
selections: ReadonlyArray<SelectionNode> = [] queryInfo: GraphQLResolveInfo
): Promise<any> { ): Promise<any> {
const dbTx = await this._database.createTransactionRunner(); const dbTx = await this._database.createTransactionRunner();
try { try {
const selections = this._getSelectionsFromGQLInfo(queryInfo);
// Get entity from the database. // Get entity from the database.
const result = await this._database.getEntityWithRelations(dbTx, entity, id, relationsMap, block, selections); const result = await this._database.getEntityWithRelations(dbTx, entity, id, relationsMap, block, selections, queryInfo);
await dbTx.commitTransaction(); await dbTx.commitTransaction();
// Resolve any field name conflicts in the entity result. // Resolve any field name conflicts in the entity result.
@ -346,7 +348,7 @@ export class GraphWatcher {
block: BlockHeight, block: BlockHeight,
where: { [key: string]: any } = {}, where: { [key: string]: any } = {},
queryOptions: QueryOptions, queryOptions: QueryOptions,
selections: ReadonlyArray<SelectionNode> = [] queryInfo: GraphQLResolveInfo
): Promise<any> { ): Promise<any> {
const dbTx = await this._database.createTransactionRunner(); const dbTx = await this._database.createTransactionRunner();
@ -357,8 +359,10 @@ export class GraphWatcher {
queryOptions.limit = DEFAULT_LIMIT; queryOptions.limit = DEFAULT_LIMIT;
} }
const selections = this._getSelectionsFromGQLInfo(queryInfo);
// Get entities from the database. // Get entities from the database.
const entities = await this._database.getEntities(dbTx, entity, relationsMap, block, where, queryOptions, selections); const entities = await this._database.getEntities(dbTx, entity, relationsMap, block, where, queryOptions, selections, queryInfo);
await dbTx.commitTransaction(); await dbTx.commitTransaction();
return entities; return entities;
@ -553,6 +557,14 @@ export class GraphWatcher {
return acc; return acc;
}, {}); }, {});
} }
_getSelectionsFromGQLInfo (queryInfo: GraphQLResolveInfo): readonly SelectionNode[] {
const [fieldNode] = queryInfo.fieldNodes;
const selectionSet = fieldNode.selectionSet;
assert(selectionSet, `selectionSet not present in GQL fieldNode ${fieldNode.name}`);
return selectionSet.selections;
}
} }
export const getGraphDbAndWatcher = async ( export const getGraphDbAndWatcher = async (

View File

@ -1,6 +1,6 @@
{ {
"name": "@cerc-io/ipld-eth-client", "name": "@cerc-io/ipld-eth-client",
"version": "0.2.88", "version": "0.2.89",
"description": "IPLD ETH Client", "description": "IPLD ETH Client",
"main": "dist/index.js", "main": "dist/index.js",
"scripts": { "scripts": {
@ -20,8 +20,8 @@
"homepage": "https://github.com/cerc-io/watcher-ts#readme", "homepage": "https://github.com/cerc-io/watcher-ts#readme",
"dependencies": { "dependencies": {
"@apollo/client": "^3.7.1", "@apollo/client": "^3.7.1",
"@cerc-io/cache": "^0.2.88", "@cerc-io/cache": "^0.2.89",
"@cerc-io/util": "^0.2.88", "@cerc-io/util": "^0.2.89",
"cross-fetch": "^3.1.4", "cross-fetch": "^3.1.4",
"debug": "^4.3.1", "debug": "^4.3.1",
"ethers": "^5.4.4", "ethers": "^5.4.4",

View File

@ -1,6 +1,6 @@
{ {
"name": "@cerc-io/peer", "name": "@cerc-io/peer",
"version": "0.2.88", "version": "0.2.89",
"description": "libp2p module", "description": "libp2p module",
"main": "dist/index.js", "main": "dist/index.js",
"exports": "./dist/index.js", "exports": "./dist/index.js",

View File

@ -1,6 +1,6 @@
{ {
"name": "@cerc-io/rpc-eth-client", "name": "@cerc-io/rpc-eth-client",
"version": "0.2.88", "version": "0.2.89",
"description": "RPC ETH Client", "description": "RPC ETH Client",
"main": "dist/index.js", "main": "dist/index.js",
"scripts": { "scripts": {
@ -19,9 +19,9 @@
}, },
"homepage": "https://github.com/cerc-io/watcher-ts#readme", "homepage": "https://github.com/cerc-io/watcher-ts#readme",
"dependencies": { "dependencies": {
"@cerc-io/cache": "^0.2.88", "@cerc-io/cache": "^0.2.89",
"@cerc-io/ipld-eth-client": "^0.2.88", "@cerc-io/ipld-eth-client": "^0.2.89",
"@cerc-io/util": "^0.2.88", "@cerc-io/util": "^0.2.89",
"chai": "^4.3.4", "chai": "^4.3.4",
"ethers": "^5.4.4", "ethers": "^5.4.4",
"left-pad": "^1.3.0", "left-pad": "^1.3.0",

View File

@ -1,6 +1,6 @@
{ {
"name": "@cerc-io/solidity-mapper", "name": "@cerc-io/solidity-mapper",
"version": "0.2.88", "version": "0.2.89",
"main": "dist/index.js", "main": "dist/index.js",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"devDependencies": { "devDependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@cerc-io/test", "name": "@cerc-io/test",
"version": "0.2.88", "version": "0.2.89",
"main": "dist/index.js", "main": "dist/index.js",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"private": true, "private": true,

View File

@ -1,6 +1,6 @@
{ {
"name": "@cerc-io/tracing-client", "name": "@cerc-io/tracing-client",
"version": "0.2.88", "version": "0.2.89",
"description": "ETH VM tracing client", "description": "ETH VM tracing client",
"main": "dist/index.js", "main": "dist/index.js",
"scripts": { "scripts": {

View File

@ -1,13 +1,13 @@
{ {
"name": "@cerc-io/util", "name": "@cerc-io/util",
"version": "0.2.88", "version": "0.2.89",
"main": "dist/index.js", "main": "dist/index.js",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {
"@apollo/utils.keyvaluecache": "^1.0.1", "@apollo/utils.keyvaluecache": "^1.0.1",
"@cerc-io/nitro-node": "^0.1.15", "@cerc-io/nitro-node": "^0.1.15",
"@cerc-io/peer": "^0.2.88", "@cerc-io/peer": "^0.2.89",
"@cerc-io/solidity-mapper": "^0.2.88", "@cerc-io/solidity-mapper": "^0.2.89",
"@cerc-io/ts-channel": "1.0.3-ts-nitro-0.1.1", "@cerc-io/ts-channel": "1.0.3-ts-nitro-0.1.1",
"@ethersproject/properties": "^5.7.0", "@ethersproject/properties": "^5.7.0",
"@ethersproject/providers": "^5.4.4", "@ethersproject/providers": "^5.4.4",
@ -52,7 +52,7 @@
"yargs": "^17.0.1" "yargs": "^17.0.1"
}, },
"devDependencies": { "devDependencies": {
"@cerc-io/cache": "^0.2.88", "@cerc-io/cache": "^0.2.89",
"@nomiclabs/hardhat-waffle": "^2.0.1", "@nomiclabs/hardhat-waffle": "^2.0.1",
"@types/bunyan": "^1.8.8", "@types/bunyan": "^1.8.8",
"@types/express": "^4.17.14", "@types/express": "^4.17.14",

View File

@ -17,7 +17,7 @@ import {
} from 'typeorm'; } from 'typeorm';
import { ColumnMetadata } from 'typeorm/metadata/ColumnMetadata'; import { ColumnMetadata } from 'typeorm/metadata/ColumnMetadata';
import { RawSqlResultsToEntityTransformer } from 'typeorm/query-builder/transformer/RawSqlResultsToEntityTransformer'; import { RawSqlResultsToEntityTransformer } from 'typeorm/query-builder/transformer/RawSqlResultsToEntityTransformer';
import { SelectionNode } from 'graphql'; import { GraphQLResolveInfo, SelectionNode } from 'graphql';
import _ from 'lodash'; import _ from 'lodash';
import debug from 'debug'; import debug from 'debug';
@ -221,7 +221,8 @@ export class GraphDatabase {
id: string, id: string,
relationsMap: Map<any, { [key: string]: any }>, relationsMap: Map<any, { [key: string]: any }>,
block: CanonicalBlockHeight = {}, block: CanonicalBlockHeight = {},
selections: ReadonlyArray<SelectionNode> = [] selections: ReadonlyArray<SelectionNode> = [],
queryInfo: GraphQLResolveInfo
): Promise<Entity | undefined> { ): Promise<Entity | undefined> {
const { hash: blockHash, number: blockNumber } = block; const { hash: blockHash, number: blockNumber } = block;
const repo = queryRunner.manager.getRepository<Entity>(entityType); const repo = queryRunner.manager.getRepository<Entity>(entityType);
@ -248,7 +249,7 @@ export class GraphDatabase {
// Get relational fields // Get relational fields
if (entityData) { if (entityData) {
entityData = await this.loadEntityRelations(queryRunner, block, relationsMap, entityType, entityData, selections); entityData = await this.loadEntityRelations(queryRunner, block, relationsMap, entityType, entityData, selections, queryInfo);
} }
return entityData; return entityData;
@ -259,7 +260,8 @@ export class GraphDatabase {
block: CanonicalBlockHeight, block: CanonicalBlockHeight,
relationsMap: Map<any, { [key: string]: any }>, relationsMap: Map<any, { [key: string]: any }>,
entityType: new () => Entity, entityData: any, entityType: new () => Entity, entityData: any,
selections: ReadonlyArray<SelectionNode> = [] selections: ReadonlyArray<SelectionNode> = [],
queryInfo: GraphQLResolveInfo
): Promise<Entity> { ): Promise<Entity> {
const relations = relationsMap.get(entityType); const relations = relationsMap.get(entityType);
if (relations === undefined) { if (relations === undefined) {
@ -292,7 +294,8 @@ export class GraphDatabase {
block, block,
where, where,
{ limit: DEFAULT_LIMIT }, { limit: DEFAULT_LIMIT },
childSelections childSelections,
queryInfo
); );
entityData[field] = relatedEntities; entityData[field] = relatedEntities;
@ -316,7 +319,8 @@ export class GraphDatabase {
block, block,
where, where,
{ limit: DEFAULT_LIMIT }, { limit: DEFAULT_LIMIT },
childSelections childSelections,
queryInfo
); );
entityData[field] = relatedEntities; entityData[field] = relatedEntities;
@ -331,7 +335,8 @@ export class GraphDatabase {
entityData[field], entityData[field],
relationsMap, relationsMap,
block, block,
childSelections childSelections,
queryInfo
); );
entityData[field] = relatedEntity; entityData[field] = relatedEntity;
@ -349,11 +354,14 @@ export class GraphDatabase {
block: CanonicalBlockHeight = {}, block: CanonicalBlockHeight = {},
where: Where = {}, where: Where = {},
queryOptions: QueryOptions = {}, queryOptions: QueryOptions = {},
selections: ReadonlyArray<SelectionNode> = [] selections: ReadonlyArray<SelectionNode> = [],
queryInfo: GraphQLResolveInfo
): Promise<Entity[]> { ): Promise<Entity[]> {
let entities: Entity[] = []; let entities: Entity[] = [];
const latestEntityType = this._entityToLatestEntityMap.get(entityType); const latestEntityType = this._entityToLatestEntityMap.get(entityType);
const defragmentedSelections = this._defragmentGQLQuerySelections(selections, queryInfo);
if (latestEntityType) { if (latestEntityType) {
if (Object.keys(block).length) { if (Object.keys(block).length) {
// Use lateral query for entities with latest entity table. // Use lateral query for entities with latest entity table.
@ -375,7 +383,7 @@ export class GraphDatabase {
relationsMap, relationsMap,
where, where,
queryOptions, queryOptions,
selections defragmentedSelections
); );
} }
} else { } else {
@ -405,7 +413,7 @@ export class GraphDatabase {
return []; return [];
} }
entities = await this.loadEntitiesRelations(queryRunner, block, relationsMap, entityType, entities, selections); entities = await this.loadEntitiesRelations(queryRunner, block, relationsMap, entityType, entities, defragmentedSelections, queryInfo);
// Resolve any field name conflicts in the entity result. // Resolve any field name conflicts in the entity result.
entities = entities.map(entity => resolveEntityFieldConflicts(entity)); entities = entities.map(entity => resolveEntityFieldConflicts(entity));
@ -744,7 +752,8 @@ export class GraphDatabase {
relationsMap: Map<any, { [key: string]: any }>, relationsMap: Map<any, { [key: string]: any }>,
entity: new () => Entity, entity: new () => Entity,
entities: Entity[], entities: Entity[],
selections: ReadonlyArray<SelectionNode> = [] selections: ReadonlyArray<SelectionNode> = [],
queryInfo: GraphQLResolveInfo
): Promise<Entity[]> { ): Promise<Entity[]> {
const relations = relationsMap.get(entity); const relations = relationsMap.get(entity);
if (relations === undefined) { if (relations === undefined) {
@ -755,11 +764,11 @@ export class GraphDatabase {
if (this._serverConfig.loadRelationsSequential) { if (this._serverConfig.loadRelationsSequential) {
for (const selection of relationSelections) { for (const selection of relationSelections) {
await this.loadRelation(queryRunner, block, relationsMap, relations, entities, selection); await this.loadRelation(queryRunner, block, relationsMap, relations, entities, selection, queryInfo);
} }
} else { } else {
const loadRelationPromises = relationSelections.map(async selection => { const loadRelationPromises = relationSelections.map(async selection => {
await this.loadRelation(queryRunner, block, relationsMap, relations, entities, selection); await this.loadRelation(queryRunner, block, relationsMap, relations, entities, selection, queryInfo);
}); });
await Promise.all(loadRelationPromises); await Promise.all(loadRelationPromises);
@ -774,7 +783,8 @@ export class GraphDatabase {
relationsMap: Map<any, { [key: string]: any }>, relationsMap: Map<any, { [key: string]: any }>,
relations: { [key: string]: any }, relations: { [key: string]: any },
entities: Entity[], entities: Entity[],
selection: SelectionNode selection: SelectionNode,
queryInfo: GraphQLResolveInfo
): Promise<void> { ): Promise<void> {
assert(selection.kind === 'Field'); assert(selection.kind === 'Field');
const field = selection.name.value; const field = selection.name.value;
@ -800,7 +810,8 @@ export class GraphDatabase {
block, block,
where, where,
{}, {},
childSelections childSelections,
queryInfo
); );
const relatedEntitiesMap = relatedEntities.reduce((acc: {[key:string]: any[]}, entity: any) => { const relatedEntitiesMap = relatedEntities.reduce((acc: {[key:string]: any[]}, entity: any) => {
@ -851,7 +862,8 @@ export class GraphDatabase {
block, block,
where, where,
{}, {},
childSelections childSelections,
queryInfo
); );
entities.forEach((entity: any) => { entities.forEach((entity: any) => {
@ -902,7 +914,8 @@ export class GraphDatabase {
block, block,
where, where,
{}, {},
childSelections childSelections,
queryInfo
); );
const relatedEntitiesMap = relatedEntities.reduce((acc: {[key:string]: any}, entity: any) => { const relatedEntitiesMap = relatedEntities.reduce((acc: {[key:string]: any}, entity: any) => {
@ -1321,4 +1334,16 @@ export class GraphDatabase {
log(`Total entities in cachedEntities.latestPrunedEntities map: ${totalEntities}`); log(`Total entities in cachedEntities.latestPrunedEntities map: ${totalEntities}`);
cachePrunedEntitiesCount.set(totalEntities); cachePrunedEntitiesCount.set(totalEntities);
} }
_defragmentGQLQuerySelections (selections: ReadonlyArray<SelectionNode>, queryInfo: GraphQLResolveInfo): SelectionNode[] {
return selections.reduce((acc: SelectionNode[], selection) => {
if (selection.kind === 'FragmentSpread') {
const fragmentSelections = queryInfo.fragments[selection.name.value].selectionSet.selections;
return [...acc, ...fragmentSelections];
}
return [...acc, selection];
}, []);
}
} }

View File

@ -18,15 +18,6 @@ const DB_SIZE_QUERY = 'SELECT pg_database_size(current_database())';
const log = debug('vulcanize:metrics'); const log = debug('vulcanize:metrics');
export async function fetchLatestBlockNumber (provider: JsonRpcProvider): Promise<number> {
try {
return await provider.getBlockNumber();
} catch (err) {
log('Error fetching latest block number', err);
return -1;
}
}
// Create custom metrics // Create custom metrics
export const lastJobCompletedOn = new client.Gauge({ export const lastJobCompletedOn = new client.Gauge({
@ -212,8 +203,12 @@ const registerUpstreamChainHeadMetrics = async ({ upstream }: Config, rpcProvide
name: 'latest_upstream_block_number', name: 'latest_upstream_block_number',
help: 'Latest upstream block number', help: 'Latest upstream block number',
async collect () { async collect () {
const latestBlockNumber = await fetchLatestBlockNumber(ethRpcProvider); try {
this.set(latestBlockNumber); const blockNumber = await ethRpcProvider.getBlockNumber();
this.set(blockNumber);
} catch (err) {
log('Error fetching latest block number', err);
}
} }
}); });
}; };