UNPKG

hamok

Version:

Lightweight Distributed Object Storage on RAFT consensus algorithm

107 lines (80 loc) 3.69 kB
/* This example demonstrates how to manage a small Hamok cluster with multiple servers, focusing on dynamic joining, leaving, and handling peer connections in a distributed environment. Workflow: 1. Initialization: - Two servers (`server_1` and `server_2`) are initialized, and their local peer IDs are logged. - The servers are set up to communicate by listening to each other's messages. 2. Joining the Cluster: - Both servers join the cluster, and a third server (`server_3`) is added to the cluster later. 3. Handling Server Leaving: - `server_1` leaves the cluster. The example monitors how `server_2` and `server_3` handle this event, including leader re-election. 4. Rejoining the Cluster: - After a pause, `server_1` re-joins the cluster. The example ensures that the rejoin event is correctly triggered and handled. 5. Final Cleanup: - The servers are gracefully closed, terminating the cluster. Practical Use: - Cluster Management: The example is useful for understanding how to manage a dynamic cluster using Hamok, including handling server failures, leader elections, and reconnections. - Event Handling: Demonstrates how to use Hamok’s event system to monitor and react to changes in the cluster. - Logging: Provides a practical example of integrating logging into a Hamok-based system for debugging and monitoring purposes. This example is suitable for developers looking to implement or understand distributed systems using the Hamok library. */ import { Hamok, setHamokLogLevel } from 'hamok'; import * as pino from 'pino'; import { HamokMessageHub } from './utils/HamokMessageHub'; const logger = pino.pino({ name: 'common-join-example-2', level: 'debug', }); export async function run() { const servers = new Map<string, Hamok>(); const messageHub = new HamokMessageHub(); const addServer = (server: Hamok) => { const leaderChangedListener = () => { logger.debug('Server %s, State: %s remotePeers: %s', server.localPeerId, server.raft.state.stateName, [...server.remotePeerIds].join(', ')); } server.once('close', () => { servers.delete(server.localPeerId); messageHub.remove(server); server.off('leader-changed', leaderChangedListener); }) servers.set(server.localPeerId, server); messageHub.add(server); server.on('leader-changed', leaderChangedListener); } addServer(new Hamok()); addServer(new Hamok()); await Promise.all([...servers.values()].map(server => server.join())); for (let i = 0; i < 10; ++i) { const newServer = new Hamok(); const oldServer = servers.values().next().value; addServer(newServer); // by having the communication channel we assume we can inquery remote endpoints const timer = setInterval(() => { const messages: string[] = []; for (const server of servers.values()) { messages.push(`server (${server.localPeerId}, state: ${server.state}) remotePeers are ${[...server.remotePeerIds].join(', ')}`); } logger.debug('iteration: %d\n, %s', i, messages.join('\n')); }, 1000) await newServer.join(); const messages: string[] = []; for (const server of servers.values()) { messages.push(`server (${server.localPeerId}, state: ${server.state}) remotePeers are ${[...server.remotePeerIds].join(', ')}`); } logger.debug('iteration: %d\n, %s', i, messages.join('\n')); await new Promise(resolve => setTimeout(resolve, 10000)); oldServer.close(); clearInterval(timer); } logger.info('Close'); for (const server of servers.values()) { server.close(); } } if (require.main === module) { logger.info('Running from module file'); setHamokLogLevel('debug'); run(); }