Skip to content
This repository has been archived by the owner on Mar 24, 2023. It is now read-only.

feat: add poly_getHealthStatus api and health-check script #497

Merged
merged 2 commits into from Aug 23, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/apis.md
Expand Up @@ -110,6 +110,7 @@ Get details at [Godwoken Docs](https://github.com/nervosnetwork/godwoken/blob/de
- poly_version
- poly_getEthTxHashByGwTxHash
- poly_getGwTxHashByEthTxHash
- poly_getHealthStatus

#### Usage

Expand Down
54 changes: 54 additions & 0 deletions docs/poly-apis.md
Expand Up @@ -29,6 +29,7 @@
* [Type `ScriptInfo`](#type-scriptinfo)
* [Type `BackendInfo`](#type-backendinfo)
* [Type `AccountInfo`](#type-accountinfo)
* [Type `HealthStatus`](#type-healthstatus)

## RPC Methods

Expand Down Expand Up @@ -409,6 +410,42 @@ Response
}
```

#### Method `poly_getHealthStatus`
* `poly_getHealthStatus()`
* result: [`HealthStatus`](#type-h256)
RetricSu marked this conversation as resolved.
Show resolved Hide resolved

Get web3 server health status

##### Examples

Request

```json
{
"id": 2,
"jsonrpc": "2.0",
"method": "poly_getHealthStatus",
"params": []
}
```

Response

```json
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"status": true,
"pingNode": "pong",
"pingFullNode": "pong",
"pingRedis": "PONG",
"isDBConnected": true,
"syncBlocksDiff": 0
}
}
```

## RPC Types

### Type `Uint32`
Expand Down Expand Up @@ -671,3 +708,20 @@ Describes the accounts web3 used.

* `defaultFrom`: [`AccountInfo`](#type-accountinfo) - Default from account used in `eth_call` and `eth_estimateGas`

### Type `HealthStatus`

Describes the web3 server health status.

#### Fields

* `status`: `boolean` - Health status, should be true

* `pingNode`: `string` - Godwoken readonly node ping result, should be "pong"

* `pingFullNode`: `string` - Godwoken fullnode node ping result, should be "pong"

* `pingRedis`: `string` - Redis server ping result, should be "PONG"

* `isDBConnected`: `boolean` - Database connection status, should be true

* `syncBlocksDiff`: `number` - Web3 sync behind godwoken blocks count, eg 2 means sync behind 2 blocks, 0 means sync to the latest
11 changes: 11 additions & 0 deletions packages/api-server/src/db/query.ts
Expand Up @@ -28,6 +28,7 @@ import {
import { LimitExceedError } from "../methods/error";
import { FilterParams } from "../base/filter";
import KnexTimeoutError = knex.KnexTimeoutError;
import { logger } from "../base/logger";

const poolMax = envConfig.pgPoolMax || 20;
const GLOBAL_KNEX = Knex({
Expand All @@ -46,6 +47,16 @@ export class Query {
this.knex = GLOBAL_KNEX;
}

async isConnected() {
try {
await this.knex.raw("SELECT 1");
return true;
} catch (error: any) {
logger.error(error.message);
return false;
}
}

async getEthTxHashByGwTxHash(gwTxHash: Hash): Promise<Hash | undefined> {
const ethTxHash = await this.knex<DBTransaction>("transactions")
.select("eth_tx_hash")
Expand Down
3 changes: 3 additions & 0 deletions packages/api-server/src/methods/constant.ts
Expand Up @@ -38,3 +38,6 @@ export const MAX_TRANSACTION_SIZE = BigInt("131072");
export const MAX_ADDRESS_SIZE_PER_REGISTER_BATCH = 50;

export const AUTO_CREATE_ACCOUNT_FROM_ID = "0x0";

// if sync behind 3 blocks, something went wrong
export const MAX_ALLOW_SYNC_BLOCKS_DIFF = 3;
44 changes: 44 additions & 0 deletions packages/api-server/src/methods/modules/poly.ts
Expand Up @@ -8,6 +8,7 @@ import { CACHE_EXPIRED_TIME_MILSECS } from "../../cache/constant";
import { Query } from "../../db";
import { ethTxHashToGwTxHash, gwTxHashToEthTxHash } from "../../cache/tx-hash";
import { middleware, validators } from "../validator";
import { MAX_ALLOW_SYNC_BLOCKS_DIFF } from "../constant";
const { version: web3Version } = require("../../../package.json");

export class Poly {
Expand Down Expand Up @@ -105,4 +106,47 @@ export class Poly {
const gwTxHash = args[0];
return await gwTxHashToEthTxHash(gwTxHash, this.query, this.cacheStore);
}

async getHealthStatus(_args: []) {
const [pingNode, pingFullNode, pingRedis, isDBConnected, syncBlocksDiff] =
await Promise.all([
this.rpc.ping(),
this.rpc.pingFullNode(),
this.cacheStore.client.PING(),
this.query.isConnected(),
this.syncBlocksDiff(),
]);

const status =
pingNode === "pong" &&
pingFullNode === "pong" &&
pingRedis === "PONG" &&
isDBConnected &&
syncBlocksDiff <= MAX_ALLOW_SYNC_BLOCKS_DIFF;

return {
status,
pingNode,
pingFullNode,
pingRedis,
isDBConnected,
syncBlocksDiff,
};
}

// get block data sync status
private async syncBlocksDiff(): Promise<Number> {
const blockNum = await this.query.getTipBlockNumber();
if (blockNum == null) {
throw new Error("db tipBlockNumber is null");
}
const dbTipBlockNumber = +blockNum.toString();

const tipBlockHash = await this.rpc.getTipBlockHash();
const tipBlock = await this.rpc.getBlock(tipBlockHash);
const gwTipBlockNumber = +tipBlock.block.raw.number;

const diff = gwTipBlockNumber - dbTipBlockNumber;
return diff;
}
}
15 changes: 15 additions & 0 deletions packages/godwoken/src/client.ts
Expand Up @@ -199,6 +199,21 @@ export class GodwokenClient {
return await this.rpcCall("get_mem_pool_state_root");
}

public async ping(): Promise<string> {
const result = await this.rpcCall("ping");
return result;
}

public async pingFullNode(): Promise<string> {
const result = await this.writeRpcCall("ping");
return result;
}

public async getBlock(blockHash: HexString): Promise<any> {
const result = await this.rpcCall("get_block", blockHash);
return result;
}

private async rpcCall(methodName: string, ...args: any[]): Promise<any> {
const name = "gw_" + methodName;
try {
Expand Down
13 changes: 13 additions & 0 deletions scripts/health-check.sh
@@ -0,0 +1,13 @@
#!/usr/bin/env bash

# Query whether web3 api is ready to serve
echo '{
"id": 42,
"jsonrpc": "2.0",
"method": "poly_getHealthStatus",
"params": []
}' \
| tr -d '\n' \
| curl --silent -H 'content-type: application/json' -d @- \
http://127.0.0.1:8024 \
| jq '.result.status' | egrep "true" || exit 1