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

Final touches #87

Merged
merged 6 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/go-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
run: go test -v -coverprofile=coverage.out ./...

- name: Upload coverage report
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage.out
path: coverage.out
55 changes: 32 additions & 23 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,26 @@ and more configurability.
Below is a list of newly added metrics (see the [README](README.md)
for metric descriptions):

* `solana_account_balance` (<u><strong>[@johnstonematt](https://github.com/johnstonematt)</strong></u>)
* `solana_node_is_healthy` (<u><strong>[@GranderStark](https://github.com/GranderStark)</strong></u>)
* `solana_nude_num_slots_behind` (<u><strong>[@GranderStark](https://github.com/GranderStark)</strong></u>)
* `solana_node_minimum_ledger_slot` (<u><strong>[@GranderStark](https://github.com/GranderStark)</strong></u>)
* `solana_node_first_available_block` (<u><strong>[@GranderStark](https://github.com/GranderStark)</strong></u>)
* `solana_cluster_slots_by_epoch_total` (<u><strong>[@johnstonematt](https://github.com/johnstonematt)</strong></u>)
* `solana_validator_fee_rewards` (<u><strong>[@johnstonematt](https://github.com/johnstonematt)</strong></u>)
* `solana_validator_block_size` (<u><strong>[@johnstonematt](https://github.com/johnstonematt)</strong></u>)
* `solana_node_block_height` (<u><strong>[@GranderStark](https://github.com/GranderStark)</strong></u>)
* `solana_cluster_active_stake` (<u><strong>[@johnstonematt](https://github.com/johnstonematt)</strong></u>)
* `solana_cluster_last_vote` (<u><strong>[@johnstonematt](https://github.com/johnstonematt)</strong></u>)
* `solana_cluster_root_slot` (<u><strong>[@johnstonematt](https://github.com/johnstonematt)</strong></u>)
* `solana_cluster_validator_count` (<u><strong>[@johnstonematt](https://github.com/johnstonematt)</strong></u>)
* `solana_account_balance` (**[@johnstonematt](https://github.com/johnstonematt)**)
* `solana_node_is_healthy` (**[@GranderStark](https://github.com/GranderStark)**)
* `solana_node_num_slots_behind` (**[@GranderStark](https://github.com/GranderStark)**)
* `solana_node_minimum_ledger_slot` (**[@GranderStark](https://github.com/GranderStark)**)
* `solana_node_first_available_block` (**[@GranderStark](https://github.com/GranderStark)**)
* `solana_cluster_slots_by_epoch_total` (**[@johnstonematt](https://github.com/johnstonematt)**)
* `solana_validator_fee_rewards` (**[@johnstonematt](https://github.com/johnstonematt)**)
* `solana_validator_block_size` (**[@johnstonematt](https://github.com/johnstonematt)**)
* `solana_node_block_height` (**[@GranderStark](https://github.com/GranderStark)**)
* `solana_cluster_active_stake` (**[@johnstonematt](https://github.com/johnstonematt)**)
* `solana_cluster_last_vote` (**[@johnstonematt](https://github.com/johnstonematt)**)
* `solana_cluster_root_slot` (**[@johnstonematt](https://github.com/johnstonematt)**)
* `solana_cluster_validator_count` (**[@johnstonematt](https://github.com/johnstonematt)**)
* `solana_node_identity` (**[@impactdni2](https://github.com/impactdni2)**)
* `solana_node_is_active` (**[@andreclaro](https://github.com/andreclaro)**)
* `solana_foundation_min_required_version` (**[@qedgardo](https://github.com/qedgardo)**)

#### Renamed Metrics

The table below contains all metrics renamed in `v3.0.0` (<u><strong>[@johnstonematt](https://github.com/johnstonematt)</strong></u>):
The table below contains all metrics renamed in `v3.0.0` (**[@johnstonematt](https://github.com/johnstonematt)**):

| Old Name | New Name |
|---------------------------------------|------------------------------------------------|
Expand All @@ -50,14 +53,14 @@ Metrics were renamed to:

#### Label Updates

The following labels were renamed (<u><strong>[@johnstonematt](https://github.com/johnstonematt)</strong></u>):
The following labels were renamed (**[@johnstonematt](https://github.com/johnstonematt)**):
* `pubkey` was renamed to `votekey`, to clearly identity that it refers to the address of a validators vote account.

### Config Updates
#### New Config Parameters

Below is a list of newly added config parameters (see the [README](README.md)
for parameter descriptions) (<u><strong>[@johnstonematt](https://github.com/johnstonematt)</strong></u>):
for parameter descriptions) (**[@johnstonematt](https://github.com/johnstonematt)**):

* `-balance-address`
* `-nodekey`
Expand All @@ -67,10 +70,12 @@ for parameter descriptions) (<u><strong>[@johnstonematt](https://github.com/john
* `-light-mode`
* `-http-timeout`
* `-comprehensive-vote-account-tracking`
* `-active-identity`
* `epoch-cleanup-time`

#### Renamed Config Parameters

The table below contains all config parameters renamed in `v3.0.0` (<u><strong>[@johnstonematt](https://github.com/johnstonematt)</strong></u>):
The table below contains all config parameters renamed in `v3.0.0` (**[@johnstonematt](https://github.com/johnstonematt)**):

| Old Name | New Name |
|-------------------------------------|-------------------|
Expand All @@ -79,21 +84,25 @@ The table below contains all config parameters renamed in `v3.0.0` (<u><strong>[

#### Removed Config Parameters

The following metrics were removed (<u><strong>[@johnstonematt](https://github.com/johnstonematt)</strong></u>):
The following metrics were removed (**[@johnstonematt](https://github.com/johnstonematt)**):

* `votepubkey`. Configure validator tracking using the `-nodekey` parameter.

### General Updates

* The project was renamed from `solana_exporter` to `solana-exporter`, to conform with
[Go naming conventions](https://github.com/unknwon/go-code-convention/blob/main/en-US.md) (<u><strong>[@johnstonematt](https://github.com/johnstonematt)</strong></u>).
* Testing was significantly improved (<u><strong>[@johnstonematt](https://github.com/johnstonematt)</strong></u>).
[Go naming conventions](https://github.com/unknwon/go-code-convention/blob/main/en-US.md) (**[@johnstonematt](https://github.com/johnstonematt)**).
* Testing was significantly improved (**[@johnstonematt](https://github.com/johnstonematt)**).
* [klog](https://github.com/kubernetes/klog) logging was removed and replaced with [zap](https://github.com/uber-go/zap)
(<u><strong>[@johnstonematt](https://github.com/johnstonematt)</strong></u>)
* Easy usage (<u><strong>[@johnstonematt](https://github.com/johnstonematt)</strong></u>):
(**[@johnstonematt](https://github.com/johnstonematt)**)
* Easy usage (**[@johnstonematt](https://github.com/johnstonematt)**):
* The example dashboard was updated.
* An example prometheus config was added, as well as recording rules for tracking skip rate.

## New Contributors

* <u><strong>[@GranderStark](https://github.com/GranderStark)</strong></u> made their first contribution.
* **[@GranderStark](https://github.com/GranderStark)** made their first contribution in **[#33](https://github.com/asymmetric-research/solana-exporter/pull/33)**.
* **[@dylanschultzie](https://github.com/dylanschultzie)** made their first contribution in **[#49](https://github.com/asymmetric-research/solana-exporter/pull/49)**.
* **[@impactdni2](https://github.com/impactdni2)** made their first contribution in **[#83](https://github.com/asymmetric-research/solana-exporter/pull/83)**.
* **[@andreclaro](https://github.com/andreclaro)** made their first contribution in **[#84](https://github.com/asymmetric-research/solana-exporter/pull/84)**.
* **[@qedgardo](https://github.com/quedgardo)** made their first contribution in **[#85](https://github.com/asymmetric-research/solana-exporter/pull/85)**.
56 changes: 51 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,60 @@ solana-exporter \
-balance-address <ADDRESS_1> -balance-address <ADDRESS_2> \
-comprehensive-slot-tracking \
-monitor-block-sizes
-active-identity <MY_ACTIVE_IDENTITY>
```

![Solana Exporter Dashboard Sample](assets/solana-dashboard-screenshot.png)

### Features
#### Balance Tracking

Using the `-balance-address <ADDRESS>` configuration parameter, the exporter can be used to monitor any account's
SOL balance.

#### Block Sizes

If the `-monitor-block-sizes` flag is set, then the exporter will export the number of transactions (both vote and
non-vote transactions) in blocks produced by the monitored validators. This is a critical validator performance metric.

Cluster average block size can be inferred by dividing total network transactions by total block height.

#### Income Reporting

The exporter exports metrics regarding total priority fee revenue and inflation reward revenue earned by the
monitored validators.

#### Skip Rate

The exporter does not directly export skip rate, as this needs to be defined as an average over a desired timeframe.
However, the exporter does track the monitored validators leader slots and whether they are `valid` or `skipped`.

The example prometheus setup contains [recording rules](prometheus/solana-rules.yml) for measuring average skip rate
for both individual validators and a cluster-level over hourly, daily and epoch intervals.

#### Active/Passive Monitoring

The `solana_node_is_active` metric simply reports whether the node (on which the exporter is running) has the same
identity-keypair as that configured with the `-active-identity` flag. The `-active-identity` flag should be used to
specify the primary identity when using a
[non-delinquent backup validator](https://pumpkins-pool.gitbook.io/pumpkins-pool).

#### Light Mode

Certain metrics, such as validator leader slots, income, block size and active stake, are visible on-chain through any
trusted node. However, other metrics such as node health and block height can only be viewed from an exporter running
on the node in question. Thus, on a node in which fine margins of performance are of critical interest, the exporter
can be set to `-light-mode`. In light mode, it will only export metrics that cannot be viewed from other nodes.

This is particularly useful in setups that contain an important validator and utility RPC node - the exporter can be
run in light mode on the validator and in full capacity on the RPC node (configured to monitor the validator through
use of the `-nodekey` parameter).

#### General Performance and Health

In addition to the above features, the exporter provides key metrics for monitoring Solana node health and performance.
See [Metrics](#metrics) below for more details.

## Installation
### Build

Expand Down Expand Up @@ -49,6 +99,7 @@ The exporter is configured via the following command line arguments:
| `-rpc-url` | Solana RPC URL (including protocol and path), e.g., `"http://localhost:8899"` or `"https://api.mainnet-beta.solana.com"` | `"http://localhost:8899"` |
| `-slot-pace` | This is the time (in seconds) between slot-watching metric collections | `1` |
| `-active-identity` | Validator identity public key used to determine if the node is considered active in the `solana_node_is_active` metric. | N/A |
| `-epoch-cleanup-time` | The time to wait before cleaning old epoch metrics from the prometheus endpoint. | |

### Notes on Configuration

Expand Down Expand Up @@ -96,11 +147,6 @@ The tables below describes all the metrics collected by the `solana-exporter`:
| `solana_node_is_active` | Whether the node is active and participating in consensus. | `identity` |
| `solana_foundation_min_required_version` | Minimum required Solana version for the [solana foundation delegation program](https://solana.org/delegation-program) | `version`, `cluster` |

#### Light Mode

In `-light-mode`, the exporter will only track metrics that uniquely to the node being queried. These metric names
all begin with `solana_node_*`.

#### Vote Account Metrics

The following metrics are all received from the `getVoteAccounts` [RPC endpoint](https://solana.com/docs/rpc/http/getvoteaccounts):
Expand Down
6 changes: 1 addition & 5 deletions pkg/rpc/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,12 @@ const (
// CommitmentProcessed level represents a transaction that has been received by the network and included in a block.
CommitmentProcessed Commitment = "processed"

// Genesis hashes for different Solana clusters
DevnetGenesisHash = "EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG"
TestnetGenesisHash = "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY"
MainnetGenesisHash = "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d"
)

// getClusterFromGenesisHash returns the cluster name based on the genesis hash
// GetClusterFromGenesisHash returns the cluster name based on the genesis hash
func GetClusterFromGenesisHash(hash string) (string, error) {
switch hash {
case DevnetGenesisHash:
Expand Down Expand Up @@ -133,7 +132,6 @@ func (c *Client) GetEpochInfo(ctx context.Context, commitment Commitment) (*Epoc
func (c *Client) GetVoteAccounts(ctx context.Context, commitment Commitment) (*VoteAccounts, error) {
// format params:
config := map[string]string{"commitment": string(commitment)}

var resp Response[VoteAccounts]
if err := getResponse(ctx, c, "getVoteAccounts", []any{config}, &resp); err != nil {
return nil, err
Expand Down Expand Up @@ -186,7 +184,6 @@ func (c *Client) GetBlockProduction(
"commitment": string(commitment),
"range": map[string]int64{"firstSlot": firstSlot, "lastSlot": lastSlot},
}

// make request:
var resp Response[contextualResult[BlockProduction]]
if err := getResponse(ctx, c, "getBlockProduction", []any{config}, &resp); err != nil {
Expand All @@ -213,7 +210,6 @@ func (c *Client) GetInflationReward(
) ([]InflationReward, error) {
// format params:
config := map[string]any{"commitment": string(commitment), "epoch": epoch}

var resp Response[[]InflationReward]
if err := getResponse(ctx, c, "getInflationReward", []any{addresses, config}, &resp); err != nil {
return nil, err
Expand Down