feat: Instead of delisting, push stale nodes to bottom (#161)

This commit is contained in:
Maciej Hirsz
2019-07-02 14:52:06 +02:00
committed by GitHub
parent c817a16d31
commit 983919fd87
8 changed files with 70 additions and 32 deletions
+3 -12
View File
@@ -29,13 +29,12 @@ export default class Chain {
} }
public get nodeCount(): Types.NodeCount { public get nodeCount(): Types.NodeCount {
return this.count as Types.NodeCount; return this.nodes.size as Types.NodeCount;
} }
public addNode(node: Node) { public addNode(node: Node) {
console.log(`[${this.label}] new node: ${node.name}`); console.log(`[${this.label}] new node: ${node.name}`);
this.count += 1;
this.nodes.add(node); this.nodes.add(node);
this.feeds.broadcast(Feed.addedNode(node)); this.feeds.broadcast(Feed.addedNode(node));
@@ -83,12 +82,7 @@ export default class Chain {
node.events.removeAllListeners(); node.events.removeAllListeners();
this.nodes.delete(node); this.nodes.delete(node);
this.feeds.broadcast(Feed.removedNode(node));
if (!node.isStale) {
this.count -= 1;
this.feeds.broadcast(Feed.removedNode(node));
}
this.events.emit('disconnect', this.nodeCount); this.events.emit('disconnect', this.nodeCount);
if (this.height === node.best.number) { if (this.height === node.best.number) {
@@ -97,8 +91,7 @@ export default class Chain {
} }
public staleNode(node: Node) { public staleNode(node: Node) {
this.count -= 1; this.feeds.broadcast(Feed.staleNode(node));
this.feeds.broadcast(Feed.removedNode(node));
if (this.height === node.best.number) { if (this.height === node.best.number) {
this.downgradeBlock(); this.downgradeBlock();
@@ -174,8 +167,6 @@ export default class Chain {
if (node.isStale) { if (node.isStale) {
node.isStale = false; node.isStale = false;
this.feeds.broadcast(Feed.addedNode(node));
this.count += 1;
} else { } else {
this.feeds.broadcast(Feed.imported(node)); this.feeds.broadcast(Feed.imported(node));
} }
+7
View File
@@ -65,6 +65,13 @@ export default class Feed {
}; };
} }
public static staleNode(node: Node): FeedMessage.Message {
return {
action: Actions.StaleNode,
payload: node.id
}
}
public static locatedNode(node: Node, location: Location): FeedMessage.Message { public static locatedNode(node: Node, location: Location): FeedMessage.Message {
return { return {
action: Actions.LocatedNode, action: Actions.LocatedNode,
+23 -16
View File
@@ -22,26 +22,27 @@ import {
} from './types'; } from './types';
export const Actions = { export const Actions = {
FeedVersion : 0x00 as 0x00, FeedVersion : 0x00 as 0x00,
BestBlock : 0x01 as 0x01, BestBlock : 0x01 as 0x01,
BestFinalized : 0x02 as 0x02, BestFinalized : 0x02 as 0x02,
AddedNode : 0x03 as 0x03, AddedNode : 0x03 as 0x03,
RemovedNode : 0x04 as 0x04, RemovedNode : 0x04 as 0x04,
LocatedNode : 0x05 as 0x05, LocatedNode : 0x05 as 0x05,
ImportedBlock : 0x06 as 0x06, ImportedBlock : 0x06 as 0x06,
FinalizedBlock : 0x07 as 0x07, FinalizedBlock : 0x07 as 0x07,
NodeStats : 0x08 as 0x08, NodeStats : 0x08 as 0x08,
NodeHardware : 0x09 as 0x09, NodeHardware : 0x09 as 0x09,
TimeSync : 0x0A as 0x0A, TimeSync : 0x0A as 0x0A,
AddedChain : 0x0B as 0x0B, AddedChain : 0x0B as 0x0B,
RemovedChain : 0x0C as 0x0C, RemovedChain : 0x0C as 0x0C,
SubscribedTo : 0x0D as 0x0D, SubscribedTo : 0x0D as 0x0D,
UnsubscribedFrom : 0x0E as 0x0E, UnsubscribedFrom : 0x0E as 0x0E,
Pong : 0x0F as 0x0F, Pong : 0x0F as 0x0F,
AfgFinalized : 0x10 as 0x10, AfgFinalized : 0x10 as 0x10,
AfgReceivedPrevote : 0x11 as 0x11, AfgReceivedPrevote : 0x11 as 0x11,
AfgReceivedPrecommit : 0x12 as 0x12, AfgReceivedPrecommit : 0x12 as 0x12,
AfgAuthoritySet : 0x13 as 0x13, AfgAuthoritySet : 0x13 as 0x13,
StaleNode : 0x14 as 0x14,
}; };
export type Action = typeof Actions[keyof typeof Actions]; export type Action = typeof Actions[keyof typeof Actions];
@@ -151,6 +152,11 @@ export namespace Variants {
action: typeof Actions.AfgReceivedPrevote; action: typeof Actions.AfgReceivedPrevote;
payload: [Address, BlockNumber, BlockHash, Address]; payload: [Address, BlockNumber, BlockHash, Address];
} }
export interface StaleNodeMessage extends MessageBase {
action: typeof Actions.StaleNode;
payload: NodeId;
}
} }
export type Message = export type Message =
@@ -173,6 +179,7 @@ export type Message =
| Variants.AfgReceivedPrevote | Variants.AfgReceivedPrevote
| Variants.AfgReceivedPrecommit | Variants.AfgReceivedPrecommit
| Variants.AfgAuthoritySet | Variants.AfgAuthoritySet
| Variants.StaleNodeMessage
| Variants.PongMessage; | Variants.PongMessage;
/** /**
+1 -1
View File
@@ -9,4 +9,4 @@ import * as FeedMessage from './feed';
export { Types, FeedMessage }; export { Types, FeedMessage };
// Increment this if breaking changes were made to types in `feed.ts` // Increment this if breaking changes were made to types in `feed.ts`
export const VERSION: Types.FeedVersion = 22 as Types.FeedVersion; export const VERSION: Types.FeedVersion = 25 as Types.FeedVersion;
+14 -1
View File
@@ -15,6 +15,8 @@ export class Connection {
return new Connection(await Connection.socket(), update, pins); return new Connection(await Connection.socket(), update, pins);
} }
private static readonly utf8decoder = new TextDecoder('utf-8');
private static readonly address = window.location.protocol === 'https:' private static readonly address = window.location.protocol === 'https:'
? `wss://${window.location.hostname}/feed/` ? `wss://${window.location.hostname}/feed/`
: `ws://${window.location.hostname}:8080`; : `ws://${window.location.hostname}:8080`;
@@ -55,6 +57,7 @@ export class Connection {
const socket = new WebSocket(Connection.address); const socket = new WebSocket(Connection.address);
socket.binaryType = "arraybuffer";
socket.addEventListener('open', onSuccess); socket.addEventListener('open', onSuccess);
socket.addEventListener('error', onFailure); socket.addEventListener('error', onFailure);
socket.addEventListener('close', onFailure); socket.addEventListener('close', onFailure);
@@ -173,6 +176,14 @@ export class Connection {
break; break;
} }
case Actions.StaleNode: {
const id = message.payload;
nodes.mutAndSort(id, (node) => node.setStale(true));
break;
}
case Actions.LocatedNode: { case Actions.LocatedNode: {
const [id, lat, lon, city] = message.payload; const [id, lat, lon, city] = message.payload;
@@ -376,7 +387,9 @@ export class Connection {
} }
private handleFeedData = (event: MessageEvent) => { private handleFeedData = (event: MessageEvent) => {
const data = event.data as FeedMessage.Data; const data = typeof event.data === 'string'
? event.data as any as FeedMessage.Data
: Connection.utf8decoder.decode(event.data) as any as FeedMessage.Data;
this.handleMessages(FeedMessage.deserialize(data)); this.handleMessages(FeedMessage.deserialize(data));
} }
@@ -60,6 +60,10 @@
color: #E6007A; color: #E6007A;
} }
.Row-stale {
font-style: italic;
}
.Row:hover { .Row:hover {
background-color: #1E1E1E; background-color: #1E1E1E;
} }
@@ -374,6 +374,10 @@ export class Row extends React.Component<Row.Props, Row.State> {
className += ' Row-pinned'; className += ' Row-pinned';
} }
if (node.stale) {
className += ' Row-stale';
}
return ( return (
<tr className={className} onClick={this.toggle}> <tr className={className} onClick={this.toggle}>
{ {
+14 -2
View File
@@ -2,7 +2,7 @@ import { Types, Maybe, SortedCollection } from '@dotstats/common';
export class Node { export class Node {
public static compare(a: Node, b: Node): number { public static compare(a: Node, b: Node): number {
if (a.pinned === b.pinned) { if (a.pinned === b.pinned && a.stale === b.stale) {
if (a.height === b.height) { if (a.height === b.height) {
const aPropagation = a.propagationTime == null ? Infinity : a.propagationTime as number; const aPropagation = a.propagationTime == null ? Infinity : a.propagationTime as number;
const bPropagation = b.propagationTime == null ? Infinity : b.propagationTime as number; const bPropagation = b.propagationTime == null ? Infinity : b.propagationTime as number;
@@ -11,7 +11,10 @@ export class Node {
return aPropagation - bPropagation; return aPropagation - bPropagation;
} }
} else { } else {
return +b.pinned - +a.pinned; const bSort = (b.pinned ? -2 : 0) + +b.stale;
const aSort = (a.pinned ? -2 : 0) + +a.stale;
return aSort - bSort;
} }
// Descending sort by block number // Descending sort by block number
@@ -26,6 +29,7 @@ export class Node {
public readonly validator: Maybe<Types.Address>; public readonly validator: Maybe<Types.Address>;
public readonly networkId: Maybe<Types.NetworkId>; public readonly networkId: Maybe<Types.NetworkId>;
public stale: boolean;
public pinned: boolean; public pinned: boolean;
public peers: Types.PeerCount; public peers: Types.PeerCount;
public txs: Types.TransactionCount; public txs: Types.TransactionCount;
@@ -110,6 +114,7 @@ export class Node {
this.blockTime = blockTime; this.blockTime = blockTime;
this.blockTimestamp = blockTimestamp; this.blockTimestamp = blockTimestamp;
this.propagationTime = propagationTime; this.propagationTime = propagationTime;
this.stale = false;
this.trigger(); this.trigger();
} }
@@ -143,6 +148,13 @@ export class Node {
} }
} }
public setStale(stale: boolean) {
if (this.stale !== stale) {
this.stale = stale;
this.trigger();
}
}
public subscribe(handler: (node: Node) => void) { public subscribe(handler: (node: Node) => void) {
this.subscriptions.add(handler); this.subscriptions.add(handler);
} }