Skip to content

Commit

Permalink
Merge pull request #2 from Cordtus/Cordtus-patch-1
Browse files Browse the repository at this point in the history
Update README.md
  • Loading branch information
Cordtus authored Jul 3, 2024
2 parents 96efde1 + afd4fb2 commit 27c8257
Showing 1 changed file with 0 additions and 188 deletions.
188 changes: 0 additions & 188 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,188 +1,3 @@
Here's the complete `balancer.ts` file updated to include better error handling and retry mechanisms for the RPC requests, along with the `speedTest` function and all the routes you originally had:

```typescript
import express from 'express';
import { crawlNetwork } from './crawler.js';
import { ChainEntry } from './types.js';
import { fetchChainData, checkAndUpdateChains } from './fetchChains.js';
import { ensureChainsFileExists, loadChainsData, saveChainsData } from './utils.js';

const app = express();
const PORT = process.env.PORT || 3000;

ensureChainsFileExists();

let chainsData: Record<string, ChainEntry> = loadChainsData();

async function updateChainData(chainName: string) {
try {
const chainData = await fetchChainData(chainName);
if (chainData) {
chainsData[chainName] = chainData;
saveChainsData(chainsData);

const initialRpcUrl = chainsData[chainName]['rpc-addresses'][0] + '/net_info';
console.log(`Starting network crawl from: ${initialRpcUrl}`);
await crawlNetwork(chainName, initialRpcUrl, 3, 0); // Add maxDepth and currentDepth
}
} catch (error) {
console.error('Error updating chain data:', error);
}
}

async function updateEndpointData(chainName: string) {
try {
const chainEntry = chainsData[chainName];
if (!chainEntry) {
console.error(`Chain ${chainName} does not exist.`);
return;
}

const initialRpcUrl = chainEntry['rpc-addresses'][0] + '/net_info';
console.log(`Starting endpoint update from: ${initialRpcUrl}`);
await crawlNetwork(chainName, initialRpcUrl, 3, 0);
} catch (error) {
console.error('Error updating endpoint data:', error);
}
}

async function speedTest(chainName: string) {
const chainEntry = chainsData[chainName];
if (!chainEntry) {
console.error(`Chain ${chainName} does not exist.`);
return;
}

const rpcAddresses = chainEntry['rpc-addresses'];
const results = [];
const exclusionList = new Set<string>();

for (const rpcAddress of rpcAddresses) {
if (exclusionList.has(rpcAddress)) {
continue;
}

try {
const startTime = Date.now();
const response = await fetch(`${rpcAddress}/status`);
const endTime = Date.now();
if (response.status === 429) {
exclusionList.add(rpcAddress);
} else if (!response.ok) {
exclusionList.add(rpcAddress);
} else {
results.push(endTime - startTime);
}
} catch (error) {
console.error(`Error testing ${rpcAddress}:`, error);
exclusionList.add(rpcAddress);
}
}

const totalRequests = results.length;
const totalTime = results.reduce((acc, curr) => acc + curr, 0);
const avgTimePerRequest = totalTime / totalRequests;

console.log(`Total requests: ${totalRequests}`);
console.log(`Average time per request: ${avgTimePerRequest} ms`);
console.log(`Requests per second: ${1000 / avgTimePerRequest}`);
}

async function proxyRequest(chain: string, endpoint: string, res: express.Response) {
const rpcAddresses = chainsData[chain]?.['rpc-addresses'];
if (!rpcAddresses || rpcAddresses.length === 0) {
return res.status(500).send('No RPC addresses available for the specified chain.');
}

let successfulResponse = false;

for (let i = 0; i < rpcAddresses.length; i++) {
const rpcAddress = rpcAddresses[i];
console.log(`Proxying request to: ${rpcAddress}/${endpoint}`);
try {
const response = await fetch(`${rpcAddress}/${endpoint}`);
const data = await response.json();
res.json(data);
successfulResponse = true;
break;
} catch (error) {
console.error(`Error proxying request to ${rpcAddress}/${endpoint}:`, error);
if (i === rpcAddresses.length - 1) {
return res.status(500).send('Error proxying request.');
}
}
}

if (!successfulResponse) {
res.status(500).send('Error proxying request.');
}
}

app.use(express.json());

app.post('/add-chain', async (req, res) => {
const { chainName } = req.body;
if (!chainName) {
return res.status(400).send('Chain name is required.');
}

if (!chainsData[chainName]) {
await updateChainData(chainName);
}

res.send('Chain added and data updated.');
});

app.post('/update-chain-data', async (req, res) => {
const { chainName } = req.body;
if (!chainName) {
return res.status(400).send('Chain name is required.');
}

await updateChainData(chainName);
res.send(`Chain data for ${chainName} updated.`);
});

app.post('/update-endpoint-data', async (req, res) => {
const { chainName } = req.body;
if (!chainName) {
return res.status(400).send('Chain name is required.');
}

await updateEndpointData(chainName);
res.send(`Endpoint data for ${chainName} updated.`);
});

app.get('/speed-test/:chainName', async (req, res) => {
const { chainName } = req.params;
await speedTest(chainName);
res.send(`Speed test for ${chainName} completed. Check logs for details.`);
});

app.get('/rpc-lb/:chain/:endpoint', async (req, res) => {
const { chain, endpoint } = req.params;

if (!chainsData[chain]) {
console.log(`Chain data for ${chain} not found, updating...`);
await updateChainData(chain);
}

await proxyRequest(chain, endpoint, res);
});

app.listen(PORT, () => {
console.log(`Load balancer running at http://localhost:${PORT}`);
setInterval(checkAndUpdateChains, 24 * 60 * 60 * 1000); // Periodic update every 24 hours
});
```

This updated `balancer.ts` file encompasses the entire functionality, including handling errors better, retrying the next endpoint upon failure, and implementing the `speedTest` function.

### Update README

Here’s how you can include the demonstration of how to run the functions in the README:

```markdown
# Load Balancer for Cosmos SDK RPC Endpoints

Set up a load balancer for many IBC networks' API endpoints using Node.js and Caddy. It dynamically fetches and caches RPC endpoint data for different chains and can be easily configured to do the same for REST or other API endpoints.
Expand Down Expand Up @@ -346,6 +161,3 @@ To send a load-balanced RPC request, send a GET request to the `/rpc-lb/<chainNa
```bash
curl http://lb.example.com/rpc-lb/akash/status
```

This interacts with the server through the reverse proxy, allowing you to manage and test the chains and endpoints dynamically.
```

0 comments on commit 27c8257

Please sign in to comment.