Accommodate GQL optimizations in codegen (#254)

* Prune optional methods in indexer and database interfaces

* Implement GQL optimization changes in codegen

* Fix graph-node test indexer

* Add demos to codegen package
This commit is contained in:
2022-11-23 17:42:25 +05:30
committed by GitHub
parent 0b33cc98c9
commit cc28474537
26 changed files with 758 additions and 57 deletions
+14
View File
@@ -253,6 +253,20 @@ export class Entity {
// Add subgraph entity specific columns.
entityObject = this._addSubgraphColumns(subgraphTypeDefs, entityObject, def);
// Add is_pruned column.
entityObject.columns.push({
name: 'isPruned',
pgType: 'boolean',
tsType: 'boolean',
columnType: 'Column',
columnOptions: [
{
option: 'default',
value: false
}
]
});
// Add decimalTransformer column option if required.
this._addDecimalTransformerOption(entityObject);
@@ -40,13 +40,12 @@ export const handler = async (argv: any): Promise<void> => {
await db.init();
{{#if (subgraphPath)}}
const graphDb = new GraphDatabase(config.server, db.baseDatabase);
await graphDb.init();
const graphWatcher = new GraphWatcher(graphDb, ethClient, ethProvider, config.server);
{{/if}}
{{/if}}
const jobQueueConfig = config.jobQueue;
assert(jobQueueConfig, 'Missing job queue config');
@@ -9,7 +9,7 @@ import assert from 'assert';
import { getConfig, initClients, JobQueue, Config, verifyCheckpointData } from '@cerc-io/util';
import { GraphWatcher, Database as GraphDatabase } from '@cerc-io/graph-node';
import { Database } from '../../database';
import { Database, ENTITY_TO_LATEST_ENTITY_MAP, ENTITY_QUERY_TYPE_MAP } from '../../database';
import { Indexer } from '../../indexer';
const log = debug('vulcanize:checkpoint-verify');
@@ -34,7 +34,7 @@ export const handler = async (argv: any): Promise<void> => {
const db = new Database(config.database);
await db.init();
const graphDb = new GraphDatabase(config.server, db.baseDatabase);
const graphDb = new GraphDatabase(config.server, db.baseDatabase, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP);
await graphDb.init();
const graphWatcher = new GraphWatcher(graphDb, ethClient, ethProvider, config.server);
@@ -7,6 +7,9 @@ import { Connection, ConnectionOptions, DeepPartial, FindConditions, QueryRunner
import path from 'path';
import { Database as BaseDatabase, DatabaseInterface, QueryOptions, StateKind, Where } from '@cerc-io/util';
{{#if (subgraphPath)}}
import { ENTITY_QUERY_TYPE } from '@cerc-io/graph-node';
{{/if}}
import { Contract } from './entity/Contract';
import { Event } from './entity/Event';
@@ -21,11 +24,23 @@ import { {{query.entityName}} } from './entity/{{query.entityName}}';
import { {{subgraphEntity.className}} } from './entity/{{subgraphEntity.className}}';
{{/each}}
export const ENTITIES = [
{{~#each queries as | query |}}{{query.entityName}}, {{/each}}
{{#if (subgraphPath)}}
export const SUBGRAPH_ENTITIES = new Set([
{{~#each subgraphEntities as | subgraphEntity |}}{{subgraphEntity.className}}
{{~#unless @last}}, {{/unless}}
{{~/each}}]);
{{/if}}
export const ENTITIES = [
{{~#if (subgraphPath)}}...SUBGRAPH_ENTITIES, {{/if}}
{{~#each queries as | query |}}{{query.entityName}}
{{~#unless @last}}, {{/unless}}
{{~/each}}];
{{#if (subgraphPath)}}
// Map: Entity to suitable query type.
export const ENTITY_QUERY_TYPE_MAP = new Map<new() => any, ENTITY_QUERY_TYPE>([]);
export const ENTITY_TO_LATEST_ENTITY_MAP: Map<any, any> = new Map();
{{/if}}
export class Database implements DatabaseInterface {
_config: ConnectionOptions;
@@ -38,6 +53,9 @@ export class Database implements DatabaseInterface {
this._config = {
...config,
{{#if (subgraphPath)}}
subscribers: [path.join(__dirname, 'entity/Subscriber.*')],
{{/if}}
entities: [path.join(__dirname, 'entity/*')]
};
@@ -24,7 +24,7 @@ import { GraphWatcher, Database as GraphDatabase } from '@cerc-io/graph-node';
{{/if}}
import * as codec from '@ipld/dag-cbor';
import { Database } from '../database';
import { Database{{#if (subgraphPath)}}, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP{{/if}} } from '../database';
import { Indexer } from '../indexer';
const log = debug('vulcanize:export-state');
@@ -59,7 +59,7 @@ const main = async (): Promise<void> => {
await db.init();
{{#if (subgraphPath)}}
const graphDb = new GraphDatabase(config.server, db.baseDatabase);
const graphDb = new GraphDatabase(config.server, db.baseDatabase, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP);
await graphDb.init();
const graphWatcher = new GraphWatcher(graphDb, ethClient, ethProvider, config.server);
@@ -7,14 +7,14 @@ import 'reflect-metadata';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import debug from 'debug';
import { PubSub } from 'graphql-subscriptions';
import { PubSub } from 'graphql-subscriptions';
import { Config, getConfig, fillBlocks, JobQueue, DEFAULT_CONFIG_PATH, initClients } from '@cerc-io/util';
{{#if (subgraphPath)}}
import { GraphWatcher, Database as GraphDatabase } from '@cerc-io/graph-node';
{{/if}}
import { Database } from './database';
import { Database{{#if (subgraphPath)}}, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP{{/if}} } from './database';
import { Indexer } from './indexer';
import { EventWatcher } from './events';
{{#if (subgraphPath)}}
@@ -72,7 +72,7 @@ export const main = async (): Promise<any> => {
await db.init();
{{#if (subgraphPath)}}
const graphDb = new GraphDatabase(config.server, db.baseDatabase);
const graphDb = new GraphDatabase(config.server, db.baseDatabase, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP);
await graphDb.init();
const graphWatcher = new GraphWatcher(graphDb, ethClient, ethProvider, config.server);
@@ -17,7 +17,7 @@ import { GraphWatcher, Database as GraphDatabase } from '@cerc-io/graph-node';
{{/if}}
import * as codec from '@ipld/dag-cbor';
import { Database } from '../database';
import { Database{{#if (subgraphPath)}}, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP{{/if}} } from '../database';
import { Indexer } from '../indexer';
import { EventWatcher } from '../events';
import { State } from '../entity/State';
@@ -50,7 +50,7 @@ export const main = async (): Promise<any> => {
await db.init();
{{#if (subgraphPath)}}
const graphDb = new GraphDatabase(config.server, db.baseDatabase);
const graphDb = new GraphDatabase(config.server, db.baseDatabase, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP);
await graphDb.init();
const graphWatcher = new GraphWatcher(graphDb, ethClient, ethProvider, config.server);
@@ -12,7 +12,7 @@ import { Config, DEFAULT_CONFIG_PATH, getConfig, initClients, JobQueue, indexBlo
import { GraphWatcher, Database as GraphDatabase } from '@cerc-io/graph-node';
{{/if}}
import { Database } from '../database';
import { Database{{#if (subgraphPath)}}, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP{{/if}} } from '../database';
import { Indexer } from '../indexer';
const log = debug('vulcanize:index-block');
@@ -44,7 +44,7 @@ const main = async (): Promise<void> => {
await db.init();
{{#if (subgraphPath)}}
const graphDb = new GraphDatabase(config.server, db.baseDatabase);
const graphDb = new GraphDatabase(config.server, db.baseDatabase, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP);
await graphDb.init();
const graphWatcher = new GraphWatcher(graphDb, ethClient, ethProvider, config.server);
@@ -41,7 +41,7 @@ import { GraphWatcher } from '@cerc-io/graph-node';
{{#each contracts as | contract |}}
import {{contract.contractName}}Artifacts from './artifacts/{{contract.contractName}}.json';
{{/each}}
import { Database, ENTITIES } from './database';
import { Database, ENTITIES{{#if (subgraphPath)}}, SUBGRAPH_ENTITIES{{/if}} } from './database';
import { createInitialState, handleEvent, createStateDiff, createStateCheckpoint } from './hooks';
import { Contract } from './entity/Contract';
import { Event } from './entity/Event';
@@ -104,17 +104,17 @@ export class Indexer implements IndexerInterface {
this._abiMap = new Map();
this._storageLayoutMap = new Map();
this._contractMap = new Map();
{{#each contracts as | contract |}}
const {
abi: {{contract.contractName}}ABI,
{{#if contract.contractStorageLayout}}
storageLayout: {{contract.contractName}}StorageLayout
{{/if}}
} = {{contract.contractName}}Artifacts;
{{/each}}
{{#each contracts as | contract |}}
assert({{contract.contractName}}ABI);
this._abiMap.set(KIND_{{capitalize contract.contractName}}, {{contract.contractName}}ABI);
{{#if contract.contractStorageLayout}}
@@ -122,9 +122,9 @@ export class Indexer implements IndexerInterface {
this._storageLayoutMap.set(KIND_{{capitalize contract.contractName}}, {{contract.contractName}}StorageLayout);
{{/if}}
this._contractMap.set(KIND_{{capitalize contract.contractName}}, new ethers.utils.Interface({{contract.contractName}}ABI));
{{/each}}
{{#if (subgraphPath)}}
this._entityTypesMap = new Map();
this._populateEntityTypesMap();
@@ -259,8 +259,10 @@ export class Indexer implements IndexerInterface {
}
async processCanonicalBlock (blockHash: string, blockNumber: number): Promise<void> {
console.time('time:indexer#processCanonicalBlock-finalize_auto_diffs');
// Finalize staged diff blocks if any.
await this._baseIndexer.finalizeDiffStaged(blockHash);
console.timeEnd('time:indexer#processCanonicalBlock-finalize_auto_diffs');
// Call custom stateDiff hook.
await createStateDiff(this, blockHash);
@@ -275,7 +277,9 @@ export class Indexer implements IndexerInterface {
const checkpointInterval = this._serverConfig.checkpointInterval;
if (checkpointInterval <= 0) return;
console.time('time:indexer#processCheckpoint-checkpoint');
await this._baseIndexer.processCheckpoint(this, blockHash, checkpointInterval);
console.timeEnd('time:indexer#processCheckpoint-checkpoint');
}
async processCLICheckpoint (contractAddress: string, blockHash?: string): Promise<string | undefined> {
@@ -308,7 +312,9 @@ export class Indexer implements IndexerInterface {
// Method used to create auto diffs (diff_staged).
async createDiffStaged (contractAddress: string, blockHash: string, data: any): Promise<void> {
console.time('time:indexer#createDiffStaged-auto_diff');
await this._baseIndexer.createDiffStaged(contractAddress, blockHash, data);
console.timeEnd('time:indexer#createDiffStaged-auto_diff');
}
// Method to be used by createStateDiff hook.
@@ -361,13 +367,25 @@ export class Indexer implements IndexerInterface {
return data;
}
async getSubgraphEntities<Entity> (
entity: new () => Entity,
block: BlockHeight,
where: { [key: string]: any } = {},
queryOptions: QueryOptions = {},
selections: ReadonlyArray<SelectionNode> = []
): Promise<any[]> {
return this._graphWatcher.getEntities(entity, this._relationsMap, block, where, queryOptions, selections);
}
{{/if}}
async triggerIndexingOnEvent (event: Event): Promise<void> {
const resultEvent = this.getResultEvent(event);
{{#if (subgraphPath)}}
console.time('time:indexer#processEvent-mapping_code');
// Call subgraph handler for event.
await this._graphWatcher.handleEvent(resultEvent);
console.timeEnd('time:indexer#processEvent-mapping_code');
{{/if}}
// Call custom hook function for indexing on event.
@@ -380,8 +398,10 @@ export class Indexer implements IndexerInterface {
}
async processBlock (blockProgress: BlockProgress): Promise<void> {
console.time('time:indexer#processBlock-init_state');
// Call a function to create initial state for contracts.
await this._baseIndexer.createInit(this, blockProgress.blockHash, blockProgress.blockNumber);
console.timeEnd('time:indexer#processBlock-init_state');
{{#if (subgraphPath)}}
this._graphWatcher.updateEntityCacheFrothyBlocks(blockProgress);
@@ -390,11 +410,15 @@ export class Indexer implements IndexerInterface {
{{#if (subgraphPath)}}
async processBlockAfterEvents (blockHash: string, blockNumber: number): Promise<void> {
console.time('time:indexer#processBlockAfterEvents-mapping_code');
// Call subgraph handler for block.
await this._graphWatcher.handleBlock(blockHash, blockNumber);
console.timeEnd('time:indexer#processBlockAfterEvents-mapping_code');
console.time('time:indexer#processBlockAfterEvents-dump_subgraph_state');
// Persist subgraph state to the DB.
await this.dumpSubgraphState(blockHash);
console.timeEnd('time:indexer#processBlockAfterEvents-dump_subgraph_state');
}
{{/if}}
@@ -557,15 +581,23 @@ export class Indexer implements IndexerInterface {
}
async markBlocksAsPruned (blocks: BlockProgress[]): Promise<void> {
return this._baseIndexer.markBlocksAsPruned(blocks);
await this._baseIndexer.markBlocksAsPruned(blocks);
{{#if (subgraphPath)}}
await this._graphWatcher.pruneEntities(FrothyEntity, blocks, SUBGRAPH_ENTITIES);
{{/if}}
}
{{#if (subgraphPath)}}
async pruneFrothyEntities (blockNumber: number): Promise<void> {
await this._graphWatcher.pruneFrothyEntities(FrothyEntity, blockNumber);
}
{{/if}}
async resetLatestEntities (blockNumber: number): Promise<void> {
await this._graphWatcher.resetLatestEntities(blockNumber);
}
{{/if}}
async updateBlockProgress (block: BlockProgress, lastProcessedEventIndex: number): Promise<BlockProgress> {
return this._baseIndexer.updateBlockProgress(block, lastProcessedEventIndex);
}
@@ -581,9 +613,13 @@ export class Indexer implements IndexerInterface {
const entities = [...ENTITIES];
{{/if}}
await this._baseIndexer.resetWatcherToBlock(blockNumber, entities);
}
{{#if (subgraphPath)}}
await this.resetLatestEntities(blockNumber);
{{/if}}
}
{{#if (subgraphPath)}}
getEntityTypesMap (): Map<string, { [key: string]: string }> {
return this._entityTypesMap;
}
@@ -28,7 +28,7 @@ import { GraphWatcher, Database as GraphDatabase } from '@cerc-io/graph-node';
{{/if}}
import { Indexer } from './indexer';
import { Database } from './database';
import { Database{{#if (subgraphPath)}}, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP{{/if}} } from './database';
const log = debug('vulcanize:job-runner');
@@ -97,7 +97,7 @@ export const main = async (): Promise<any> => {
await db.init();
{{#if (subgraphPath)}}
const graphDb = new GraphDatabase(config.server, db.baseDatabase);
const graphDb = new GraphDatabase(config.server, db.baseDatabase, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP);
await graphDb.init();
const graphWatcher = new GraphWatcher(graphDb, ethClient, ethProvider, config.server);
@@ -12,10 +12,12 @@ import { ValueResult, BlockHeight, gqlTotalQueryCount, gqlQueryCount, jsonBigInt
import { Indexer } from './indexer';
import { EventWatcher } from './events';
{{#if (subgraphPath)}}
{{#each subgraphQueries as | query |}}
import { {{query.entityName}} } from './entity/{{query.entityName}}';
{{/each}}
{{/if}}
const log = debug('vulcanize:resolver');
@@ -20,7 +20,7 @@ import { GraphWatcher, Database as GraphDatabase } from '@cerc-io/graph-node';
import { createResolvers } from './resolvers';
import { Indexer } from './indexer';
import { Database } from './database';
import { Database{{#if (subgraphPath)}}, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP{{/if}} } from './database';
import { EventWatcher } from './events';
const log = debug('vulcanize:server');
@@ -45,7 +45,7 @@ export const main = async (): Promise<any> => {
await db.init();
{{#if (subgraphPath)}}
const graphDb = new GraphDatabase(config.server, db.baseDatabase);
const graphDb = new GraphDatabase(config.server, db.baseDatabase, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP);
await graphDb.init();
const graphWatcher = new GraphWatcher(graphDb, ethClient, ethProvider, config.server);
@@ -5,16 +5,16 @@
import { EventSubscriber, EntitySubscriberInterface, InsertEvent, UpdateEvent } from 'typeorm';
import { FrothyEntity } from './FrothyEntity';
import { ENTITIES } from '../database';
import { ENTITY_TO_LATEST_ENTITY_MAP, SUBGRAPH_ENTITIES } from '../database';
import { afterEntityInsertOrUpdate } from '@cerc-io/graph-node';
@EventSubscriber()
export class EntitySubscriber implements EntitySubscriberInterface {
async afterInsert (event: InsertEvent<any>): Promise<void> {
await afterEntityInsertOrUpdate(FrothyEntity, ENTITIES, event);
await afterEntityInsertOrUpdate(FrothyEntity, SUBGRAPH_ENTITIES, event, ENTITY_TO_LATEST_ENTITY_MAP);
}
async afterUpdate (event: UpdateEvent<any>): Promise<void> {
await afterEntityInsertOrUpdate(FrothyEntity, ENTITIES, event);
await afterEntityInsertOrUpdate(FrothyEntity, SUBGRAPH_ENTITIES, event, ENTITY_TO_LATEST_ENTITY_MAP);
}
}
@@ -1,8 +1,8 @@
//
// Copyright 2021 Vulcanize, Inc.
//
{{#each types as | type |}}
export enum {{type.name}} {
{{#each type.values as | value |}}
{{value}} = '{{value}}',