14 KiB
Network Library Phase 3: Message Routing Implementation Plan
Having successfully completed Phase 2 with the Transport Layer implementation, we're now ready to move on to Phase 3, which focuses on Message Routing. This phase will build upon our transport capabilities to implement intelligent routing of messages across the network.
Overview
Phase 3 focuses on creating a robust message routing system that can efficiently deliver messages between nodes, even when there's no direct connection between sender and recipient. This includes message prioritization, route discovery, and quality-of-service features.
Implementation Goals
- Message Router Framework: Create the core routing infrastructure
- Routing Algorithms: Implement multiple routing strategies
- Prioritization System: Build a quality-of-service framework
- Message Batching: Optimize network usage with message grouping
- Route Discovery: Implement mechanisms to find optimal paths
- Plugin Extension Points: Allow customization of routing behaviors
Detailed Implementation Plan
1. Message Router Core
First, we'll define the interfaces and base classes for the routing system:
// src/router/types.ts
export interface RouteInfo {
nextHopId: string;
pathLength: number;
estimatedLatency: number;
reliability: number;
lastUpdated: number;
}
export interface RoutingTable {
getRoute(targetId: string): RouteInfo | null;
addRoute(targetId: string, route: RouteInfo): void;
removeRoute(targetId: string): boolean;
getAllRoutes(): Map<string, RouteInfo>;
clear(): void;
}
export interface IMessageRouter {
routeMessage(message: NetworkMessage): Promise<void>;
addRoutingTable(table: RoutingTable): void;
findRoute(targetId: string): RouteInfo | null;
updateRoutes(): Promise<void>;
}
export enum RoutingStrategy {
DIRECT = 'direct',
FLOOD = 'flood',
SHORTEST_PATH = 'shortest-path',
RANDOM = 'random',
CUSTOM = 'custom'
}
export interface RoutingConfig {
strategy: RoutingStrategy;
prioritization: boolean;
batchingEnabled: boolean;
batchInterval: number;
maxBatchSize: number;
retryCount: number;
routeUpdateInterval: number;
}
2. Basic Routing Table Implementation
Next, we'll implement a basic routing table:
// src/router/routing-table.ts
export class BasicRoutingTable implements RoutingTable {
private _routes: Map<string, RouteInfo> = new Map();
getRoute(targetId: string): RouteInfo | null {
return this._routes.get(targetId) || null;
}
addRoute(targetId: string, route: RouteInfo): void {
this._routes.set(targetId, route);
}
removeRoute(targetId: string): boolean {
return this._routes.delete(targetId);
}
getAllRoutes(): Map<string, RouteInfo> {
return new Map(this._routes);
}
clear(): void {
this._routes.clear();
}
// Additional utility methods for route optimization
getBestRoute(targetId: string, criteria: 'latency' | 'reliability' | 'pathLength'): RouteInfo | null {
// Implementation
}
pruneStaleRoutes(maxAge: number): number {
// Implementation
}
}
3. Base Message Router
Now we'll implement the base message router class:
// src/router/base-message-router.ts
export abstract class BaseMessageRouter implements IMessageRouter {
protected _routingTables: RoutingTable[] = [];
protected _node: NetworkNode;
protected _config: RoutingConfig;
protected _messageQueue: PriorityQueue<NetworkMessage>;
protected _batchingEnabled: boolean;
constructor(node: NetworkNode, config?: Partial<RoutingConfig>) {
this._node = node;
this._config = { ...this._getDefaultConfig(), ...config };
this._batchingEnabled = this._config.batchingEnabled;
this._messageQueue = new PriorityQueue<NetworkMessage>((a, b) =>
(a.priority || MessagePriority.NORMAL) - (b.priority || MessagePriority.NORMAL)
);
// Set up processing interval if batching is enabled
if (this._batchingEnabled) {
this._setupBatchProcessing();
}
}
abstract routeMessage(message: NetworkMessage): Promise<void>;
addRoutingTable(table: RoutingTable): void {
this._routingTables.push(table);
}
findRoute(targetId: string): RouteInfo | null {
// Check each routing table for a route
for (const table of this._routingTables) {
const route = table.getRoute(targetId);
if (route) {
return route;
}
}
return null;
}
abstract updateRoutes(): Promise<void>;
protected _getDefaultConfig(): RoutingConfig {
return {
strategy: RoutingStrategy.DIRECT,
prioritization: true,
batchingEnabled: false,
batchInterval: 100,
maxBatchSize: 10,
retryCount: 3,
routeUpdateInterval: 30000 // 30 seconds
};
}
protected _setupBatchProcessing(): void {
// Set up interval to process message batches
}
protected async _processBatch(): Promise<void> {
// Process a batch of messages based on priority
}
}
4. Routing Strategy Implementations
We'll implement multiple routing strategies:
// src/router/strategies/direct-router.ts
export class DirectMessageRouter extends BaseMessageRouter {
async routeMessage(message: NetworkMessage): Promise<void> {
// Direct delivery to target if connected
if (this._node.transport && message.targetId) {
await this._node.transport.sendTo(message.targetId, message);
} else {
throw new Error('Cannot route message: no transport or target ID');
}
}
async updateRoutes(): Promise<void> {
// Direct routing doesn't need route updates
return Promise.resolve();
}
}
// src/router/strategies/flood-router.ts
export class FloodMessageRouter extends BaseMessageRouter {
async routeMessage(message: NetworkMessage): Promise<void> {
// Send to all connected nodes except source
const sentTo = new Set<string>();
// Send to parent if exists
if (this._node.parent && this._node.parent.id !== message.senderId) {
await this._node.parent.sendMessage(message);
sentTo.add(this._node.parent.id);
}
// Send to all children
for (const child of this._node.children) {
if (child.id !== message.senderId && !sentTo.has(child.id)) {
await child.sendMessage(message);
sentTo.add(child.id);
}
}
// Send to siblings
for (const sibling of this._node.siblings) {
if (sibling.id !== message.senderId && !sentTo.has(sibling.id)) {
await sibling.sendMessage(message);
sentTo.add(sibling.id);
}
}
}
async updateRoutes(): Promise<void> {
// Flooding doesn't need route updates
return Promise.resolve();
}
}
5. Message Prioritization System
Next, we'll implement message prioritization for quality-of-service:
// src/router/priority-queue.ts
export class PriorityQueue<T> {
private _items: T[] = [];
private _comparator: (a: T, b: T) => number;
constructor(comparator: (a: T, b: T) => number) {
this._comparator = comparator;
}
enqueue(item: T): void {
this._items.push(item);
this._items.sort(this._comparator);
}
dequeue(): T | undefined {
return this._items.shift();
}
peek(): T | undefined {
return this._items[0];
}
get size(): number {
return this._items.length;
}
isEmpty(): boolean {
return this._items.length === 0;
}
clear(): void {
this._items = [];
}
}
// src/router/message-batching.ts
export class MessageBatch {
messages: NetworkMessage[] = [];
priority: MessagePriority;
timestamp: number;
constructor(priority: MessagePriority = MessagePriority.NORMAL) {
this.priority = priority;
this.timestamp = Date.now();
}
addMessage(message: NetworkMessage): void {
this.messages.push(message);
}
get size(): number {
return this.messages.length;
}
get averagePriority(): number {
if (this.messages.length === 0) return this.priority;
const sum = this.messages.reduce((acc, msg) =>
acc + (msg.priority || MessagePriority.NORMAL), 0);
return sum / this.messages.length;
}
}
6. Route Discovery System
We'll implement route discovery and optimization:
// src/router/route-discovery.ts
export class RouteDiscoveryManager {
private _node: NetworkNode;
private _routingTable: RoutingTable;
private _discoveryInProgress: boolean = false;
constructor(node: NetworkNode, routingTable: RoutingTable) {
this._node = node;
this._routingTable = routingTable;
}
async discoverRoutes(): Promise<void> {
if (this._discoveryInProgress) return;
this._discoveryInProgress = true;
try {
// Send discovery messages to immediate connections
await this._sendDiscoveryRequests();
// Wait for responses
await this._collectResponses();
// Update routing table
this._updateRoutingTable();
} finally {
this._discoveryInProgress = false;
}
}
private async _sendDiscoveryRequests(): Promise<void> {
// Implementation
}
private async _collectResponses(): Promise<void> {
// Implementation
}
private _updateRoutingTable(): void {
// Implementation
}
}
7. Advanced Router with Plugin Support
Finally, we'll create an advanced router that supports plugins:
// src/router/advanced-message-router.ts
export interface RouterPlugin {
id: string;
onMessageRouting(message: NetworkMessage, nextHop: string | null): Promise<string | null>;
onRoutingError(error: Error, message: NetworkMessage): Promise<boolean>;
onRouteDiscovery(routes: Map<string, RouteInfo>): Promise<void>;
}
export class AdvancedMessageRouter extends BaseMessageRouter {
private _plugins: RouterPlugin[] = [];
private _discoveryManager: RouteDiscoveryManager;
constructor(node: NetworkNode, config?: Partial<RoutingConfig>) {
super(node, config);
const routingTable = new BasicRoutingTable();
this._routingTables.push(routingTable);
this._discoveryManager = new RouteDiscoveryManager(node, routingTable);
// Set up periodic route updates
if (this._config.routeUpdateInterval > 0) {
setInterval(() => this.updateRoutes(), this._config.routeUpdateInterval);
}
}
async routeMessage(message: NetworkMessage): Promise<void> {
// If batching is enabled, queue the message
if (this._batchingEnabled) {
this._messageQueue.enqueue(message);
return;
}
// Otherwise, route immediately
await this._routeSingleMessage(message);
}
async updateRoutes(): Promise<void> {
await this._discoveryManager.discoverRoutes();
}
registerPlugin(plugin: RouterPlugin): void {
this._plugins.push(plugin);
}
unregisterPlugin(pluginId: string): boolean {
const index = this._plugins.findIndex(p => p.id === pluginId);
if (index >= 0) {
this._plugins.splice(index, 1);
return true;
}
return false;
}
private async _routeSingleMessage(message: NetworkMessage): Promise<void> {
// Find the route
let nextHop = this._findNextHop(message);
// Allow plugins to modify the route
for (const plugin of this._plugins) {
nextHop = await plugin.onMessageRouting(message, nextHop) || nextHop;
}
// If we have a next hop, send the message
if (nextHop && this._node.transport) {
try {
await this._node.transport.sendTo(nextHop, message);
} catch (error) {
// Let plugins handle the error
let handled = false;
for (const plugin of this._plugins) {
handled = await plugin.onRoutingError(error as Error, message) || handled;
}
// If not handled by plugins, rethrow
if (!handled) {
throw error;
}
}
} else {
throw new Error('No route found for message');
}
}
private _findNextHop(message: NetworkMessage): string | null {
if (!message.targetId) {
return null;
}
// Direct delivery if we're connected to the target
if (this._node.transport && this._node.transport.state === TransportState.CONNECTED) {
// Check if target is a direct connection
return message.targetId;
}
// Otherwise, look up in routing table
const route = this.findRoute(message.targetId);
if (route) {
return route.nextHopId;
}
return null;
}
}
Testing Plan
-
Unit Tests:
- Test routing table operations
- Test message prioritization
- Test different routing strategies
- Test plugin integration
-
Integration Tests:
- Test end-to-end message routing
- Test route discovery
- Test fallback behaviors
- Test with different network topologies
-
Performance Tests:
- Test routing with high message volumes
- Test batching performance
- Test priority queuing under load
Implementation Schedule
- Week 1: Implement routing interfaces and basic routing table
- Week 2: Implement base router and direct routing strategy
- Week 3: Implement flooding and shortest-path strategies
- Week 4: Implement prioritization and batching
- Week 5: Implement route discovery
- Week 6: Implement advanced router with plugin support
Conclusion
This plan outlines the implementation of Phase 3, focusing on the Message Routing Layer. This builds upon the Transport Layer completed in Phase 2 and will provide the foundation for more advanced network capabilities in future phases. The modular design with plugin support ensures we can extend functionality as needed.