Skip to content

Commit

Permalink
common: move grafting items to a dedicated module
Browse files Browse the repository at this point in the history
  • Loading branch information
tilacog committed Oct 24, 2023
1 parent 1e6582c commit 7f17f7d
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 95 deletions.
2 changes: 1 addition & 1 deletion packages/indexer-common/src/__tests__/grafting.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { resolveGrafting, GraftableSubgraph } from '../subgraphs'
import { resolveGrafting, GraftableSubgraph } from '../grafting'
import { SubgraphDeploymentID } from '@graphprotocol/common-ts'

// Create a mock for the fetchSubgraphManifest function
Expand Down
93 changes: 93 additions & 0 deletions packages/indexer-common/src/grafting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { SubgraphDeploymentID } from '@graphprotocol/common-ts'
import { GraphNodeInterface } from './graph-node'
import { BlockPointer, SubgraphManifest } from './types'
import { indexerError, IndexerErrorCode } from './errors'

export interface GraftBase {
block: number
base: SubgraphDeploymentID
}

export interface GraftableSubgraph {
deployment: SubgraphDeploymentID
graft: GraftBase | null // Root subgraph does not have a graft base
}

type SubgraphManifestResolver = (
subgraphID: SubgraphDeploymentID,
) => Promise<SubgraphManifest>

interface IndexingStatus {
latestBlock: BlockPointer | null
health: string
synced: boolean
}

interface SubgraphGraftStatus extends GraftableSubgraph {
indexingStatus: IndexingStatus | null
}

// Resolves all graft dependencies for a given subgraph
export async function resolveGrafting(
subgraphManifestResolver: SubgraphManifestResolver,
targetDeployment: SubgraphDeploymentID,
maxIterations: number = 100,
): Promise<GraftableSubgraph[]> {
const graftBases: GraftableSubgraph[] = []

let iterationCount = 0
let deployment = targetDeployment
while (iterationCount < maxIterations) {
const manifest = await subgraphManifestResolver(deployment)
let graft: GraftBase | null = null
if (manifest.features?.includes('grafting') && manifest.graft) {
// Found a graft base
const base = new SubgraphDeploymentID(manifest.graft.base)
graft = { block: manifest.graft.block, base }
graftBases.push({ deployment, graft })
deployment = base
} else {
// Reached root subgraph, stop iterating
iterationCount = maxIterations
graftBases.push({ deployment, graft })
}
iterationCount++
}

// Check if we have found the graft root
if (graftBases.length > 0 && graftBases[graftBases.length - 1].graft !== null) {
throw new Error(
`Failed to find a graft root for target subgraph deployment (${targetDeployment.ipfsHash}) after ${iterationCount} iterations.`,
)
}

return graftBases
}

export async function getIndexingStatusOfGraftableSubgraph(
subgraph: GraftableSubgraph,
graphNode: GraphNodeInterface,
): Promise<SubgraphGraftStatus> {
let response
try {
response = await graphNode.indexingStatus([subgraph.deployment])
} catch (error) {
const message = `Failed to fetch indexing status when resolving subgraph grafts`
// TODO: log this error
throw indexerError(IndexerErrorCode.IE075, { message, error })
}
let indexingStatus: IndexingStatus | null = null
if (response && response.length) {
const subgraphIndexingStatus = response[0]
let latestBlock: BlockPointer | null = null
if (subgraphIndexingStatus.chains && subgraphIndexingStatus.chains.length) {
latestBlock = subgraphIndexingStatus.chains[0].latestBlock
}
indexingStatus = {
health: subgraphIndexingStatus.health,
synced: subgraphIndexingStatus.synced,
latestBlock,
}
}
return { ...subgraph, indexingStatus }
}
95 changes: 1 addition & 94 deletions packages/indexer-common/src/subgraphs.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { base58 } from 'ethers/lib/utils'
import { BigNumber, utils } from 'ethers'
import { Logger, SubgraphDeploymentID } from '@graphprotocol/common-ts'
import { SubgraphDeployment, SubgraphManifestSchema, BlockPointer } from './types'
import { SubgraphDeployment, SubgraphManifest, SubgraphManifestSchema } from './types'
import {
INDEXING_RULE_GLOBAL,
IndexingDecisionBasis,
Expand All @@ -13,8 +13,6 @@ import { QueryResult } from './network-subgraph'
import { mergeSelectionSets, sleep } from './utils'
import * as yaml from 'yaml'
import { indexerError, IndexerErrorCode } from './errors'
import { GraphNodeInterface } from './graph-node'
import { z } from 'zod'

export enum SubgraphIdentifierType {
DEPLOYMENT = 'deployment',
Expand Down Expand Up @@ -528,59 +526,6 @@ export class SubgraphFreshnessChecker {
}
}

export interface GraftBase {
block: number
base: SubgraphDeploymentID
}

export interface GraftableSubgraph {
deployment: SubgraphDeploymentID
graft: GraftBase | null // Root subgraph does not have a graft base
}

type SubgraphManifestResolver = (
subgraphID: SubgraphDeploymentID,
) => Promise<SubgraphManifest>

// Resolves all graft dependencies for a given subgraph
export async function resolveGrafting(
subgraphManifestResolver: SubgraphManifestResolver,
targetDeployment: SubgraphDeploymentID,
maxIterations: number = 100,
): Promise<GraftableSubgraph[]> {
const graftBases: GraftableSubgraph[] = []

let iterationCount = 0
let deployment = targetDeployment
while (iterationCount < maxIterations) {
const manifest = await subgraphManifestResolver(deployment)
let graft: GraftBase | null = null
if (manifest.features?.includes('grafting') && manifest.graft) {
// Found a graft base
const base = new SubgraphDeploymentID(manifest.graft.base)
graft = { block: manifest.graft.block, base }
graftBases.push({ deployment, graft })
deployment = base
} else {
// Reached root subgraph, stop iterating
iterationCount = maxIterations
graftBases.push({ deployment, graft })
}
iterationCount++
}

// Check if we have found the graft root
if (graftBases.length > 0 && graftBases[graftBases.length - 1].graft !== null) {
throw new Error(
`Failed to find a graft root for target subgraph deployment (${targetDeployment.ipfsHash}) after ${iterationCount} iterations.`,
)
}

return graftBases
}

type SubgraphManifest = z.infer<typeof SubgraphManifestSchema>

// Simple fetch for subgraph manifest
export async function fetchSubgraphManifest(
ipfsEndpoint: URL,
Expand Down Expand Up @@ -641,41 +586,3 @@ export async function fetchSubgraphManifest(
throw indexerError(IndexerErrorCode.IE075, message)
}
}

interface IndexingStatus {
latestBlock: BlockPointer | null
health: string
synced: boolean
}

interface SubgraphGraftStatus extends GraftableSubgraph {
indexingStatus: IndexingStatus | null
}

export async function getIndexingStatusOfGraftableSubgraph(
subgraph: GraftableSubgraph,
graphNode: GraphNodeInterface,
): Promise<SubgraphGraftStatus> {
let response
try {
response = await graphNode.indexingStatus([subgraph.deployment])
} catch (error) {
const message = `Failed to fetch indexing status when resolving subgraph grafts`
// TODO: log this error
throw indexerError(IndexerErrorCode.IE075, { message, error })
}
let indexingStatus: IndexingStatus | null = null
if (response && response.length) {
const subgraphIndexingStatus = response[0]
let latestBlock: BlockPointer | null = null
if (subgraphIndexingStatus.chains && subgraphIndexingStatus.chains.length) {
latestBlock = subgraphIndexingStatus.chains[0].latestBlock
}
indexingStatus = {
health: subgraphIndexingStatus.health,
synced: subgraphIndexingStatus.synced,
latestBlock,
}
}
return { ...subgraph, indexingStatus }
}
2 changes: 2 additions & 0 deletions packages/indexer-common/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,5 @@ export const SubgraphManifestSchema = z.object({
features: z.array(z.string()).optional(),
graft: Graft.optional(),
})

export type SubgraphManifest = z.infer<typeof SubgraphManifestSchema>

0 comments on commit 7f17f7d

Please sign in to comment.