Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add script to suspend Indexers #829

Merged
merged 9 commits into from
Jun 25, 2024
3 changes: 2 additions & 1 deletion runner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"start:dev": "tsc -p ./tsconfig.build.json && node ./dist",
"start:docker": "node dist/index.js",
"test": "npm run codegen && node --experimental-vm-modules ./node_modules/.bin/jest --silent",
"lint": "eslint -c .eslintrc.js"
"lint": "eslint -c .eslintrc.js",
"script:suspend-indexer": "tsc -p ./tsconfig.json && node ./dist/scripts/suspend-indexer.js"
},
"keywords": [],
"author": "",
Expand Down
100 changes: 100 additions & 0 deletions runner/scripts/suspend-indexer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* This script is used to suspend an indexer for a given account. It will:
* 1. Call Coordinator to disable the indexer
* 2. Write to the Indexers logs table to notify of suspension
*
* Note that as Coordinator is in a private network, you must tunnel to the machine to expose the gRPC server.
* This can be achieved via running the following in a separate terminal:
* ```sh
* gcloud compute ssh ubuntu@queryapi-coordinator-mainnet -- -L 9003:0.0.0.0:9003
* ```
*
* The following environment variables are required:
* - `HASURA_ADMIN_SECRET`
* - `HASURA_ENDPOINT`
* - `PGPORT`
* - `PGHOST`
*
* All of which can be found in the Runner compute instance metadata:
* ```sh
* gcloud compute instances describe queryapi-runner-mainnet
* ```
*
*
* Usage: npm run script:suspend-indexer -- <accountId> <functionName>
*/

import assert from 'assert'
import * as fs from 'fs'

import * as grpc from '@grpc/grpc-js'
import * as protoLoader from '@grpc/proto-loader'

import Provisioner from '../src/provisioner'
import IndexerConfig from '../src/indexer-config'
import IndexerMeta, { LogEntry } from '../src/indexer-meta';

const COORDINATOR_PROTO_PATH = '../coordinator/proto/indexer_manager.proto';

assert(exists(COORDINATOR_PROTO_PATH), 'Coordinator proto file not found. Make sure you run this script from the root directory.');
assert(process.argv.length === 4, 'Usage: npm run script:suspend-indexer -- <accountId> <functionName>');
assert(process.env.COORDINATOR_PORT, 'COORDINATOR_PORT env var is required');
assert(process.env.HASURA_ADMIN_SECRET, 'HASURA_ADMIN_SECRET env var is required');
assert(process.env.HASURA_ENDPOINT, 'HASURA_ENDPOINT env var is required');
assert(process.env.PGPORT, 'PGPORT env var is required');
assert(process.env.PGHOST, 'PGHOST env var is required');

const [_binary, _file, accountId, functionName] = process.argv;
const { COORDINATOR_PORT = 9003 } = process.env;

main();

async function main() {
await suspendIndexer();
await logSuspension();

console.log('Done')
}

async function logSuspension() {
console.log('Logging suspension notification');

const config = new IndexerConfig('not needed', accountId, functionName, 0, 'not needed', 'not needed', 2);

const pgCredentials = await new Provisioner().getPostgresConnectionParameters(config.userName());

await new IndexerMeta(config, pgCredentials).writeLogs([
LogEntry.systemInfo('The indexer is suspended due to inactivity.'),
]);
}

async function suspendIndexer() {
console.log(`Suspending indexer: ${accountId}/${functionName}`);

const indexerManager = createIndexerManagerClient();

return new Promise((resolve, reject) => {
indexerManager.disable({ accountId, functionName }, (err: any, response: any) => {
if (err) {
reject(err);
} else {
resolve(response);
}
});
})
}

function exists(path: string): boolean {
try {
fs.statSync(path);
return true;
} catch (err) {
return false;
}
}

function createIndexerManagerClient() {
const packageDefinition = protoLoader.loadSync(COORDINATOR_PROTO_PATH);
const protoDescriptor: any = grpc.loadPackageDefinition(packageDefinition);
return new protoDescriptor.indexer.IndexerManager(`localhost:${COORDINATOR_PORT}`, grpc.credentials.createInsecure());
}
1 change: 1 addition & 0 deletions runner/src/indexer-meta/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { default } from './indexer-meta';
export { IndexerStatus, METADATA_TABLE_UPSERT, MetadataFields } from './indexer-meta';
export { default as LogEntry } from './log-entry';
2 changes: 1 addition & 1 deletion runner/tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"extends": "./tsconfig.json",
"include": ["./src"],
"exclude": ["node_modules", "dist", "**/*.test.*"]
"exclude": ["node_modules", "dist", "**/*.test.*", "scripts"]
}
4 changes: 2 additions & 2 deletions runner/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"target": "es2018", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"lib": ["es2021"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
"module": "commonjs", /* Specify what module code is generated. */
"rootDirs": ["./src", "./tests"],
"rootDirs": ["./src", "./tests", "./scripts"],
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
"resolveJsonModule": true, /* Enable importing .json files. */
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
Expand All @@ -20,6 +20,6 @@
"noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
},
"include": ["./src", "./tests"],
"include": ["./src", "./tests", "./scripts"],
"exclude": ["node_modules", "dist"]
}
Loading