From 302db9b21bb3a0b595c6b53bd549c2a5111dc656 Mon Sep 17 00:00:00 2001 From: tilacog Date: Tue, 24 Oct 2023 18:33:13 -0300 Subject: [PATCH] common: implement resolveGraftedSubgraphDeployment function --- packages/indexer-common/src/grafting.ts | 87 +++++++++++++++++++++---- packages/indexer-common/src/types.ts | 12 ++++ 2 files changed, 87 insertions(+), 12 deletions(-) diff --git a/packages/indexer-common/src/grafting.ts b/packages/indexer-common/src/grafting.ts index 1211c8a0a..e4a115a83 100644 --- a/packages/indexer-common/src/grafting.ts +++ b/packages/indexer-common/src/grafting.ts @@ -1,8 +1,23 @@ import { SubgraphDeploymentID } from '@graphprotocol/common-ts' import { GraphNodeInterface } from './graph-node' -import { BlockPointer, SubgraphManifest } from './types' +import { + BlockPointer, + SubgraphDeploymentDecision, + SubgraphDeploymentDecisionKind, + SubgraphManifest, +} from './types' import { indexerError, IndexerErrorCode } from './errors' +type SubgraphManifestResolver = ( + subgraphID: SubgraphDeploymentID, +) => Promise + +interface IndexingStatus { + latestBlock: BlockPointer | null + health: string + synced: boolean +} + export interface GraftBase { block: number base: SubgraphDeploymentID @@ -13,18 +28,17 @@ export interface GraftableSubgraph { graft: GraftBase | null // Root subgraph does not have a graft base } -type SubgraphManifestResolver = ( - subgraphID: SubgraphDeploymentID, -) => Promise - -interface IndexingStatus { - latestBlock: BlockPointer | null - health: string - synced: boolean +interface GraftableSubgraphStatus extends GraftableSubgraph { + indexingStatus: IndexingStatus | null } -interface SubgraphGraftStatus extends GraftableSubgraph { - indexingStatus: IndexingStatus | null +// TODO: use this type instead of a plain list. +// Benefits: No need to check for graft base block on the adjacent sibling. +interface SubgraphGraftLineage { + target: SubgraphDeploymentID + root: GraftBase + // list of descending graft bases, except the root. + bases: GraftBase[] } // Discovers all graft dependencies for a given subgraph @@ -64,7 +78,7 @@ export async function discoverGraftBases( export async function getIndexingStatusOfGraftableSubgraph( subgraph: GraftableSubgraph, graphNode: GraphNodeInterface, -): Promise { +): Promise { let response try { response = await graphNode.indexingStatus([subgraph.deployment]) @@ -88,3 +102,52 @@ export async function getIndexingStatusOfGraftableSubgraph( } return { ...subgraph, indexingStatus } } + +export function resolveGraftedSubgraphDeployment( + subgraphLineage: GraftableSubgraphStatus[], +): SubgraphDeploymentDecision[] { + const deploymentDecisions: SubgraphDeploymentDecision[] = [] + + // Check lineage size before making any assumptions. + if (subgraphLineage.length < 2) { + throw new Error( + `Invalid input: Expected at least two members in graft lineage but got ${subgraphLineage.length}`, + ) + } + // Check for any unsynced base. + // Iterate backwards while ignoring the target deployment (first element). + for (let i = subgraphLineage.length - 1; i > 1; i--) { + const graft = subgraphLineage[i] + + // Block height is stored in the previous element in the lineage list. + // Since we are skipping the root (last element), the graft info is expected to be present. + const desiredBlockHeight = subgraphLineage[i - 1].graft!.block + + if (!graft.indexingStatus || !graft.indexingStatus.latestBlock) { + // Graph Node is not aware of this subgraph deployment. We must deploy it and look no further. + deploymentDecisions.push({ + deployment: graft.deployment, + deploymentDecision: SubgraphDeploymentDecisionKind.DEPLOY, + }) + break + } else { + // Deployment exists. + + // Is it sufficiently synced? + if (graft.indexingStatus.latestBlock.number >= desiredBlockHeight) { + // If so, we can stop syncing it. + deploymentDecisions.push({ + deployment: graft.deployment, + deploymentDecision: SubgraphDeploymentDecisionKind.REMOVE, + }) + continue + } + + // Is it healthy? + if (graft.indexingStatus.health !== 'healthy') { + throw new Error(`Unhealthy graft base: ${graft.deployment}`) + } + } + } + return deploymentDecisions +} diff --git a/packages/indexer-common/src/types.ts b/packages/indexer-common/src/types.ts index c0885a60d..d5896f05b 100644 --- a/packages/indexer-common/src/types.ts +++ b/packages/indexer-common/src/types.ts @@ -120,3 +120,15 @@ export const SubgraphManifestSchema = z.object({ }) export type SubgraphManifest = z.infer + +export enum SubgraphDeploymentDecisionKind { + CREATE = 'create', + DEPLOY = 'deploy', + REMOVE = 'remove', + // Possible new members: PAUSE, DROP, NOOP +} + +export interface SubgraphDeploymentDecision { + deployment: SubgraphDeploymentID + deploymentDecision: SubgraphDeploymentDecisionKind +}