Skip to content

Commit

Permalink
indexer-common,indexer-agent: auto graft resolve
Browse files Browse the repository at this point in the history
  • Loading branch information
hopeyen committed Mar 31, 2023
1 parent 708464b commit 87ba4f0
Show file tree
Hide file tree
Showing 13 changed files with 284 additions and 11 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,12 @@ Indexer Infrastructure
--auto-allocation-min-batch-size Minimum number of allocation
transactions inside a batch for AUTO
management mode [number] [default: 1]
--auto-graft-resolver-limit Maximum depth of grafting dependency to
automatically
resolve [number] [default: 0]
--ipfs-endpoint Endpoint to an ipfs node to quickly
query subgraph manifest data` [string]
[default: "https://ipfs.network.thegraph.com"]
Network Subgraph
--network-subgraph-deployment Network subgraph deployment [string]
Expand Down
11 changes: 10 additions & 1 deletion docs/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -886,4 +886,13 @@ This is a sub-error of `IE069`. It is reported when the indexer agent doesn't ha

**Solution**

Please provide a `epoch-subgraph-endpoint` and make sure graph node has consistent network configurations (`mainnet`, `goerli`, `gnosis`) and is on or after version 0.28.0.
## IE072

**Summary**

Please provide a `epoch-subgraph-endpoint` and make sure graph node has consistent network configurations (`mainnet`, `goerli`, `gnosis`) and is on or after version 0.28.0.
Failed to deploy subgraph deployment graft base.

**Solution**

Please make sure the auto graft depth resolver has correct limit, and that the graft base deployment has synced to the graft block before trying again - Set indexing rules for agent to periodically reconcile the deployment.
6 changes: 6 additions & 0 deletions packages/indexer-agent/src/__tests__/indexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ const setup = async () => {
await sequelize.sync({ force: true })

const statusEndpoint = 'http://localhost:8030/graphql'
const ipfsEndpoint = 'https://ipfs.network.thegraph.com'
const indexingStatusResolver = new IndexingStatusResolver({
logger: logger,
statusEndpoint: 'statusEndpoint',
Expand All @@ -139,6 +140,7 @@ const setup = async () => {
})

const indexNodeIDs = ['node_1']
const autoGraftResolverLimit = 1
indexerManagementClient = await createIndexerManagementClient({
models,
address: toAddress(address),
Expand All @@ -157,6 +159,8 @@ const setup = async () => {
features: {
injectDai: false,
},
ipfsEndpoint,
autoGraftResolverLimit,
})

indexer = new Indexer(
Expand All @@ -168,6 +172,8 @@ const setup = async () => {
parseGRT('1000'),
address,
AllocationManagementMode.AUTO,
ipfsEndpoint,
autoGraftResolverLimit,
)
}

Expand Down
6 changes: 5 additions & 1 deletion packages/indexer-agent/src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,11 @@ class Agent {
// Ensure the deployment is deployed to the indexer
// Note: we're not waiting here, as sometimes indexing a subgraph
// will block if the IPFS files cannot be retrieved
this.indexer.ensure(name, deployment)
try {
this.indexer.ensure(name, deployment)
} catch {
this.indexer.resolveGrafting(name, deployment, 0)
}
}),
)

Expand Down
22 changes: 22 additions & 0 deletions packages/indexer-agent/src/commands/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,18 @@ export default {
default: 100,
group: 'Query Fees',
})
.option('ipfs-endpoint', {
description: `Endpoint to an ipfs node to quickly query subgraph manifest data`,
type: 'string',
default: 'https://ipfs.network.thegraph.com',
group: 'Indexer Infrastructure',
})
.option('auto-graft-resolver-limit', {
description: `Maximum depth of grafting dependency to automatically resolve`,
type: 'number',
default: 0,
group: 'Indexer Infrastructure',
})
.option('inject-dai', {
description:
'Inject the GRT to DAI/USDC conversion rate into cost model variables',
Expand Down Expand Up @@ -370,6 +382,12 @@ export default {
) {
return 'Invalid --rebate-claim-max-batch-size provided. Must be > 0 and an integer.'
}
if (
!Number.isInteger(argv['auto-graft-resolver-limit']) ||
argv['auto-graft-resolver-limit'] < 0
) {
return 'Invalid --auto-graft-resolver-limit provided. Must be >= 0 and an integer.'
}
return true
})
.option('collect-receipts-endpoint', {
Expand Down Expand Up @@ -693,6 +711,8 @@ export default {
networkMonitor,
allocationManagementMode,
autoAllocationMinBatchSize: argv.autoAllocationMinBatchSize,
ipfsEndpoint: argv.ipfsEndpoint,
autoGraftResolverLimit: argv.autoGraftResolverLimit,
})

await createIndexerManagementServer({
Expand All @@ -711,6 +731,8 @@ export default {
argv.defaultAllocationAmount,
indexerAddress,
allocationManagementMode,
argv.ipfsEndpoint,
argv.autoGraftResolverLimit,
)

if (networkSubgraphDeploymentId !== undefined) {
Expand Down
81 changes: 80 additions & 1 deletion packages/indexer-agent/src/indexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
} from '@graphprotocol/indexer-common'
import { CombinedError } from '@urql/core'
import pMap from 'p-map'
import yaml from 'yaml'

const POI_DISPUTES_CONVERTERS_FROM_GRAPHQL: Record<
keyof POIDisputeAttributes,
Expand Down Expand Up @@ -84,6 +85,8 @@ export class Indexer {
defaultAllocationAmount: BigNumber
indexerAddress: string
allocationManagementMode: AllocationManagementMode
ipfsEndpoint: string
autoGraftResolverLimit: number

constructor(
logger: Logger,
Expand All @@ -94,12 +97,16 @@ export class Indexer {
defaultAllocationAmount: BigNumber,
indexerAddress: string,
allocationManagementMode: AllocationManagementMode,
ipfsUrl: string,
autoGraftResolverLimit: number,
) {
this.indexerManagement = indexerManagement
this.statusResolver = statusResolver
this.logger = logger
this.indexerAddress = indexerAddress
this.allocationManagementMode = allocationManagementMode
this.autoGraftResolverLimit = autoGraftResolverLimit
this.ipfsEndpoint = ipfsUrl + '/api/v0/cat?arg='

if (adminEndpoint.startsWith('https')) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down Expand Up @@ -735,6 +742,78 @@ export class Indexer {
}
}

// Simple fetch for subgraph manifest
async subgraphManifest(targetDeployment: SubgraphDeploymentID) {
const ipfsFile = await fetch(
this.ipfsEndpoint + targetDeployment.ipfsHash,
{
method: 'POST',
redirect: 'follow',
},
)
return yaml.parse(await ipfsFile.text())
}

// Recursive function for targetDeployment resolve grafting, add depth until reached to resolverDepth
async resolveGrafting(
name: string,
targetDeployment: SubgraphDeploymentID,
depth: number,
): Promise<void> {
// Matches "/depth-" followed by one or more digits
const depthRegex = /\/depth-\d+/
const manifest = await this.subgraphManifest(targetDeployment)

// No grafting dependency
if (!manifest.features || !manifest.features.includes('grafting')) {
// Ensure sync if at root of dependency
if (depth) {
await this.ensure(name, targetDeployment)
}
return
}

// Default autoGraftResolverLimit is 0, essentially disabling auto-resolve
if (depth >= this.autoGraftResolverLimit) {
throw indexerError(
IndexerErrorCode.IE074,
`Grafting depth reached limit for auto resolve`,
)
}

try {
const baseDeployment = new SubgraphDeploymentID(manifest.graft.base)
let baseName = name.replace(depthRegex, `/depth-${depth}`)
if (baseName === name) {
// add depth suffix if didn't have one from targetDeployment
baseName += `/depth-${depth}`
}
await this.resolveGrafting(baseName, baseDeployment, depth + 1)

// If base deployment has synced upto the graft block, then ensure the target deployment
// Otherwise just log to come back later
const graftStatus = await this.statusResolver.indexingStatus([
baseDeployment,
])
// If base deployment synced to required block, try to sync the target and
// turn off syncing for the base deployment
if (
graftStatus[0].chains[0].latestBlock &&
graftStatus[0].chains[0].latestBlock.number >= manifest.graft.block
) {
await this.ensure(name, targetDeployment)
} else {
this.logger.debug(
`Graft base deployment has yet to reach the graft block, try again later`,
)
}
} catch {
throw indexerError(
IndexerErrorCode.IE074,
`Base deployment hasn't synced to the graft block, try again later`,
)
}
}
async deploy(
name: string,
deployment: SubgraphDeploymentID,
Expand All @@ -751,7 +830,7 @@ export class Indexer {
node_id: node_id,
})
if (response.error) {
throw response.error
throw indexerError(IndexerErrorCode.IE026, response.error)
}
this.logger.info(`Successfully deployed subgraph deployment`, {
name,
Expand Down
2 changes: 2 additions & 0 deletions packages/indexer-common/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export enum IndexerErrorCode {
IE071 = 'IE071',
IE072 = 'IE072',
IE073 = 'IE073',
IE074 = 'IE074',
}

export const INDEXER_ERROR_MESSAGES: Record<IndexerErrorCode, string> = {
Expand Down Expand Up @@ -161,6 +162,7 @@ export const INDEXER_ERROR_MESSAGES: Record<IndexerErrorCode, string> = {
IE071: 'Add Epoch subgraph support for non-protocol chains',
IE072: 'Failed to execute batch tx (contract: staking)',
IE073: 'Failed to query subgraph features from indexing statuses endpoint',
IE074: 'Failed to deploy the graft base for the target deployment',
}

export type IndexerErrorCause = unknown
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ const setup = async () => {
injectDai: true,
},
})
mockedSubgraphManager = new SubgraphManager('fake endpoint', ['fake node id'])
mockedSubgraphManager = new SubgraphManager('fake endpoint', ['fake node id'], indexingStatusResolver)
allocationManager = new AllocationManager(
contracts,
logger,
Expand Down
8 changes: 8 additions & 0 deletions packages/indexer-common/src/indexer-management/allocations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,14 @@ export class AllocationManager {
deployment.ipfsHash,
)

// Ensure graft dependency is resolved
await this.subgraphManager.resolveGrafting(
logger,
this.models,
deployment,
indexNode,
0,
)
// Ensure subgraph is deployed before allocating
await this.subgraphManager.ensure(
logger,
Expand Down
12 changes: 11 additions & 1 deletion packages/indexer-common/src/indexer-management/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,8 @@ export interface IndexerManagementClientOptions {
networkMonitor?: NetworkMonitor
allocationManagementMode?: AllocationManagementMode
autoAllocationMinBatchSize?: number
ipfsEndpoint?: string
autoGraftResolverLimit?: number
}

export class IndexerManagementClient extends Client {
Expand Down Expand Up @@ -522,6 +524,8 @@ export const createIndexerManagementClient = async (
networkMonitor,
allocationManagementMode,
autoAllocationMinBatchSize,
ipfsEndpoint,
autoGraftResolverLimit,
} = options
const schema = buildSchema(print(SCHEMA_SDL))
const resolvers = {
Expand All @@ -535,7 +539,13 @@ export const createIndexerManagementClient = async (

const dai: WritableEventual<string> = mutable()

const subgraphManager = new SubgraphManager(deploymentManagementEndpoint, indexNodeIDs)
const subgraphManager = new SubgraphManager(
deploymentManagementEndpoint,
indexNodeIDs,
indexingStatusResolver,
ipfsEndpoint,
autoGraftResolverLimit,
)
let allocationManager: AllocationManager | undefined = undefined
let actionManager: ActionManager | undefined = undefined

Expand Down
6 changes: 3 additions & 3 deletions packages/indexer-common/src/indexer-management/monitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -630,9 +630,9 @@ export class NetworkMonitor {

if (!result.data.network) {
// If the network is missing, it means it is not registered in the Epoch Subgraph.
throw new Error(
`Failed to query EBO for ${networkID}'s latest valid epoch number: Network not found`,
)
const err = `Failed to query EBO for ${networkID}'s latest valid epoch number: Network not found`
this.logger.error(err)
throw indexerError(IndexerErrorCode.IE071, err)
}

if (!result.data.network.latestValidBlockNumber) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,14 @@ export default {
subgraphDeploymentID.ipfsHash,
)

// Ensure grafting dependencies are resolved
await subgraphManager.resolveGrafting(
logger,
models,
subgraphDeploymentID,
indexNode,
0,
)
// Ensure subgraph is deployed before allocating
await subgraphManager.ensure(
logger,
Expand Down
Loading

0 comments on commit 87ba4f0

Please sign in to comment.