dashboard, p2p: separate active peers from inactive ones

This commit is contained in:
Kurkó Mihály 2018-11-14 22:39:52 +02:00
parent 92d5937023
commit 547bad0d60
12 changed files with 13589 additions and 13946 deletions

File diff suppressed because one or more lines are too long

View file

@ -23,6 +23,7 @@ import TableHead from '@material-ui/core/TableHead';
import TableBody from '@material-ui/core/TableBody'; import TableBody from '@material-ui/core/TableBody';
import TableRow from '@material-ui/core/TableRow'; import TableRow from '@material-ui/core/TableRow';
import TableCell from '@material-ui/core/TableCell'; import TableCell from '@material-ui/core/TableCell';
import Grid from '@material-ui/core/Grid/Grid';
import AreaChart from 'recharts/es6/chart/AreaChart'; import AreaChart from 'recharts/es6/chart/AreaChart';
import Tooltip from 'recharts/es6/component/Tooltip'; import Tooltip from 'recharts/es6/component/Tooltip';
import Area from 'recharts/es6/cartesian/Area'; import Area from 'recharts/es6/cartesian/Area';
@ -57,13 +58,13 @@ export const inserter = (sampleLimit: number) => (update: NetworkType, prev: Net
delete bundle.knownPeers[event.id]; delete bundle.knownPeers[event.id];
return; return;
} }
case 'unknown': { case 'attempt': {
const bundle = prev.peers.bundles[event.ip]; const bundle = prev.peers.bundles[event.ip];
if (!bundle || !Array.isArray(bundle.unknownPeers) || bundle.unknownPeers.length < 1) { if (!bundle || !Array.isArray(bundle.attempts) || bundle.attempts.length < 1) {
console.error('No unknown peer to remove', event.ip); console.error('No unknown peer to remove', event.ip);
return; return;
} }
bundle.unknownPeers.splice(0, 1); bundle.attempts.splice(0, 1);
return; return;
} }
} }
@ -76,7 +77,7 @@ export const inserter = (sampleLimit: number) => (update: NetworkType, prev: Net
longitude: 0, longitude: 0,
}, },
knownPeers: {}, knownPeers: {},
unknownPeers: [], attempts: [],
}; };
} }
const bundle = prev.peers.bundles[event.ip]; const bundle = prev.peers.bundles[event.ip];
@ -85,13 +86,13 @@ export const inserter = (sampleLimit: number) => (update: NetworkType, prev: Net
return; return;
} }
if (!event.id) { if (!event.id) {
bundle.unknownPeers.push({ bundle.attempts.push({
connected: event.connected, connected: event.connected,
disconnected: event.disconnected, disconnected: event.disconnected,
}); });
return; return;
} }
if (!bundle.knownPeers[event.id]) { if (!bundle.knownPeers || !bundle.knownPeers[event.id]) {
bundle.knownPeers[event.id] = { bundle.knownPeers[event.id] = {
connected: [], connected: [],
disconnected: [], disconnected: [],
@ -138,7 +139,7 @@ type State = {};
// Network renders the network page. // Network renders the network page.
class Network extends Component<Props, State> { class Network extends Component<Props, State> {
formatTime = (t) => { formatTime = (t: string) => {
const time = new Date(t); const time = new Date(t);
if (isNaN(time)) { if (isNaN(time)) {
return ''; return '';
@ -153,16 +154,14 @@ class Network extends Component<Props, State> {
render() { render() {
return ( return (
<div> <Grid container direction='row' justify='space-between' spacing={24}>
<Grid item xs={6}>
<Table> <Table>
<TableHead> <TableHead>
<TableRow> <TableRow>
<TableCell>IP</TableCell>
<TableCell>Location</TableCell>
<TableCell>Node ID</TableCell> <TableCell>Node ID</TableCell>
<TableCell>Location</TableCell>
<TableCell>Traffic</TableCell> <TableCell>Traffic</TableCell>
<TableCell>Connected</TableCell>
<TableCell>Disconnected</TableCell>
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableBody> <TableBody>
@ -171,22 +170,21 @@ class Network extends Component<Props, State> {
return null; return null;
} }
return ( return (
<TableRow key={`known${ip}`}> <TableRow key={`known_${ip}`}>
<TableCell>{ip}</TableCell> <TableCell>
{Object.keys(bundle.knownPeers).map(id => id.substring(0, 10)).join(' ')}
</TableCell>
<TableCell> <TableCell>
{bundle.location ? (() => { {bundle.location ? (() => {
const l = bundle.location; const l = bundle.location;
return `${l.country ? l.country : ''}${l.city ? `/${l.city}` : ''} ${l.latitude} ${l.longitude}`; return `${l.country ? l.country : ''}${l.city ? `/${l.city}` : ''}`;
})() : ''} })() : ''}
</TableCell> </TableCell>
<TableCell>
{Object.keys(bundle.knownPeers).map(id => id.substring(0, 10)).join(' ')}
</TableCell>
<TableCell> <TableCell>
{Object.values(bundle.knownPeers).map(({ingress, egress}) => ( {Object.values(bundle.knownPeers).map(({ingress, egress}) => (
<div> <div>
<AreaChart <AreaChart
width={300} height={50} width={200} height={18}
syncId={'footerSyncId'} syncId={'footerSyncId'}
data={egress.map(({value}) => ({egress: value || 0}))} data={egress.map(({value}) => ({egress: value || 0}))}
margin={{top: 5, right: 5, bottom: 0, left: 5}} margin={{top: 5, right: 5, bottom: 0, left: 5}}
@ -195,7 +193,7 @@ class Network extends Component<Props, State> {
<Area isAnimationActive={false} type='monotone' dataKey='egress' stroke='#8884d8' fill='#8884d8' /> <Area isAnimationActive={false} type='monotone' dataKey='egress' stroke='#8884d8' fill='#8884d8' />
</AreaChart> </AreaChart>
<AreaChart <AreaChart
width={300} height={50} width={200} height={18}
syncId={'footerSyncId'} syncId={'footerSyncId'}
data={ingress.map(({value}) => ({ingress: -value || 0}))} data={ingress.map(({value}) => ({ingress: -value || 0}))}
margin={{top: 0, right: 5, bottom: 5, left: 5}} margin={{top: 0, right: 5, bottom: 5, left: 5}}
@ -206,52 +204,46 @@ class Network extends Component<Props, State> {
</div> </div>
))} ))}
</TableCell> </TableCell>
<TableCell>
{Object.values(bundle.knownPeers).map(peer => peer.connected && peer.connected.map(time => this.formatTime(time)).join(' ')).join(', ')}
</TableCell>
<TableCell>
{Object.values(bundle.knownPeers).map(peer => peer.disconnected && peer.disconnected.map(time => this.formatTime(time)).join(' ')).join(', ')}
</TableCell>
</TableRow> </TableRow>
); );
})} })}
</TableBody> </TableBody>
</Table> </Table>
</Grid>
<Grid item xs={6}>
<Table> <Table>
<TableHead> <TableHead>
<TableRow> <TableRow>
<TableCell>IP</TableCell> <TableCell>IP</TableCell>
<TableCell>Location</TableCell> <TableCell>Location</TableCell>
<TableCell>Connected</TableCell> <TableCell>Attempts</TableCell>
<TableCell>Disconnected</TableCell>
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableBody> <TableBody>
{Object.entries(this.props.content.peers.bundles).map(([ip, bundle]) => { {Object.entries(this.props.content.peers.bundles).map(([ip, bundle]) => {
if (!bundle.unknownPeers || bundle.unknownPeers.length < 1) { if (!bundle.attempts || bundle.attempts.length < 1) {
return null; return null;
} }
return ( return (
<TableRow key={`unknown${ip}`}> <TableRow key={`attempt_${ip}`}>
<TableCell>{ip}</TableCell> <TableCell>{ip}</TableCell>
<TableCell> <TableCell>
{bundle.location ? (() => { {bundle.location ? (() => {
const l = bundle.location; const l = bundle.location;
return `${l.country ? l.country : ''}${l.city ? `/${l.city}` : ''} ${l.latitude} ${l.longitude}`; return `${l.country ? l.country : ''}${l.city ? `/${l.city}` : ''}`;
})() : ''} })() : ''}
</TableCell> </TableCell>
<TableCell> <TableCell>
{Object.values(bundle.unknownPeers).map(peer => peer.connected && `${this.formatTime(peer.connected)}`).join(', ')} {Object.values(bundle.attempts).length}
</TableCell>
<TableCell>
{Object.values(bundle.unknownPeers).map(peer => peer.disconnected && `${this.formatTime(peer.disconnected)}`).join(', ')}
</TableCell> </TableCell>
</TableRow> </TableRow>
); );
})} })}
</TableBody> </TableBody>
</Table> </Table>
</div>); </Grid>
</Grid>
);
} }
} }

View file

@ -25,6 +25,9 @@ import createMuiTheme from '@material-ui/core/styles/createMuiTheme';
import Dashboard from './components/Dashboard'; import Dashboard from './components/Dashboard';
const theme: Object = createMuiTheme({ const theme: Object = createMuiTheme({
// typography: {
// useNextVariants: true,
// },
palette: { palette: {
type: 'dark', type: 'dark',
}, },

View file

@ -1,48 +1,48 @@
{ {
"private": true, "private": true,
"dependencies": { "dependencies": {
"@babel/core": "7.1.2", "@babel/core": "^7.2.2",
"@babel/plugin-proposal-class-properties": "7.1.0", "@babel/plugin-proposal-class-properties": "7.2.3",
"@babel/plugin-proposal-function-bind": "^7.0.0", "@babel/plugin-proposal-function-bind": "7.2.0",
"@babel/plugin-transform-flow-strip-types": "^7.0.0", "@babel/plugin-transform-flow-strip-types": "7.2.3",
"@babel/preset-env": "7.1.0", "@babel/preset-env": "7.2.3",
"@babel/preset-react": "^7.0.0", "@babel/preset-react": "^7.0.0",
"@babel/preset-stage-0": "^7.0.0", "@babel/preset-stage-0": "^7.0.0",
"@material-ui/core": "3.2.0", "@material-ui/core": "3.8.1",
"@material-ui/icons": "^3.0.1", "@material-ui/icons": "^3.0.1",
"babel-eslint": "10.0.1", "babel-eslint": "10.0.1",
"babel-loader": "8.0.4", "babel-loader": "8.0.5",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"css-loader": "^1.0.0", "css-loader": "2.1.0",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"eslint": "5.7.0", "eslint": "5.11.1",
"eslint-config-airbnb": "^17.0.0", "eslint-config-airbnb": "^17.0.0",
"eslint-loader": "2.1.1", "eslint-loader": "2.1.1",
"eslint-plugin-flowtype": "3.0.0", "eslint-plugin-flowtype": "3.2.0",
"eslint-plugin-import": "^2.13.0", "eslint-plugin-import": "^2.13.0",
"eslint-plugin-jsx-a11y": "6.1.2", "eslint-plugin-jsx-a11y": "6.1.2",
"eslint-plugin-node": "^7.0.1", "eslint-plugin-node": "8.0.0",
"eslint-plugin-promise": "4.0.1", "eslint-plugin-promise": "4.0.1",
"eslint-plugin-react": "7.11.1", "eslint-plugin-react": "7.12.2",
"file-loader": "2.0.0", "file-loader": "3.0.1",
"flow-bin": "0.83.0", "flow-bin": "0.89.0",
"flow-bin-loader": "^1.0.3", "flow-bin-loader": "^1.0.3",
"flow-typed": "^2.5.1", "flow-typed": "^2.5.1",
"path": "^0.12.7", "path": "^0.12.7",
"react": "16.5.2", "react": "16.7.0",
"react-dom": "16.5.2", "react-dom": "16.7.0",
"react-fa": "^5.0.0", "react-fa": "^5.0.0",
"react-hot-loader": "4.3.11", "react-hot-loader": "4.6.3",
"react-transition-group": "2.5.0", "react-transition-group": "2.5.2",
"recharts": "1.3.4", "recharts": "1.4.2",
"style-loader": "0.23.1", "style-loader": "0.23.1",
"uglifyjs-webpack-plugin": "2.0.1", "uglifyjs-webpack-plugin": "2.1.1",
"url": "^0.11.0", "url": "^0.11.0",
"url-loader": "1.1.2", "url-loader": "1.1.2",
"webpack": "4.20.2", "webpack": "4.28.3",
"webpack-cli": "3.1.2", "webpack-cli": "3.2.0",
"webpack-dev-server": "3.1.9", "webpack-dev-server": "3.1.14",
"webpack-merge": "^4.1.4" "webpack-merge": "4.1.5"
}, },
"scripts": { "scripts": {
"build": "webpack --config webpack.config.prod.js", "build": "webpack --config webpack.config.prod.js",

View file

@ -29,7 +29,6 @@ export type Content = {
export type ChartEntries = Array<ChartEntry>; export type ChartEntries = Array<ChartEntry>;
export type ChartEntry = { export type ChartEntry = {
time: Date,
value: number, value: number,
}; };
@ -73,7 +72,7 @@ export type Peers = {
export type PeerBundle = { export type PeerBundle = {
location: GeoLocation, location: GeoLocation,
knownPeers: {[string]: KnownPeer}, knownPeers: {[string]: KnownPeer},
unknownPeers: Array<UnknownPeer>, attempts: Array<UnknownPeer>,
}; };
export type KnownPeer = { export type KnownPeer = {

File diff suppressed because it is too large Load diff

View file

@ -60,7 +60,7 @@ type Dashboard struct {
peerLock sync.RWMutex // Lock protecting the stored peer data peerLock sync.RWMutex // Lock protecting the stored peer data
logLock sync.RWMutex // Lock protecting the stored log data logLock sync.RWMutex // Lock protecting the stored log data
geodb *GeoDB // geoip database instance for IP to geographical information conversions geodb *geoDB // geoip database instance for IP to geographical information conversions
logdir string // Directory containing the log files logdir string // Directory containing the log files
quit chan chan error // Channel used for graceful exit quit chan chan error // Channel used for graceful exit
@ -91,14 +91,14 @@ func New(config *Config, commit string, logdir string) *Dashboard {
Version: fmt.Sprintf("v%d.%d.%d%s", params.VersionMajor, params.VersionMinor, params.VersionPatch, versionMeta), Version: fmt.Sprintf("v%d.%d.%d%s", params.VersionMajor, params.VersionMinor, params.VersionPatch, versionMeta),
}, },
System: &SystemMessage{ System: &SystemMessage{
ActiveMemory: emptyChartEntries(now, sampleLimit, config.Refresh), ActiveMemory: emptyChartEntries(now, sampleLimit),
VirtualMemory: emptyChartEntries(now, sampleLimit, config.Refresh), VirtualMemory: emptyChartEntries(now, sampleLimit),
NetworkIngress: emptyChartEntries(now, sampleLimit, config.Refresh), NetworkIngress: emptyChartEntries(now, sampleLimit),
NetworkEgress: emptyChartEntries(now, sampleLimit, config.Refresh), NetworkEgress: emptyChartEntries(now, sampleLimit),
ProcessCPU: emptyChartEntries(now, sampleLimit, config.Refresh), ProcessCPU: emptyChartEntries(now, sampleLimit),
SystemCPU: emptyChartEntries(now, sampleLimit, config.Refresh), SystemCPU: emptyChartEntries(now, sampleLimit),
DiskRead: emptyChartEntries(now, sampleLimit, config.Refresh), DiskRead: emptyChartEntries(now, sampleLimit),
DiskWrite: emptyChartEntries(now, sampleLimit, config.Refresh), DiskWrite: emptyChartEntries(now, sampleLimit),
}, },
}, },
logdir: logdir, logdir: logdir,
@ -106,12 +106,10 @@ func New(config *Config, commit string, logdir string) *Dashboard {
} }
// emptyChartEntries returns a ChartEntry array containing limit number of empty samples. // emptyChartEntries returns a ChartEntry array containing limit number of empty samples.
func emptyChartEntries(t time.Time, limit int, refresh time.Duration) ChartEntries { func emptyChartEntries(t time.Time, limit int) ChartEntries {
ce := make(ChartEntries, limit) ce := make(ChartEntries, limit)
for i := 0; i < limit; i++ { for i := 0; i < limit; i++ {
ce[i] = &ChartEntry{ ce[i] = new(ChartEntry)
Time: t.Add(-time.Duration(i) * refresh),
}
} }
return ce return ce
} }

View file

@ -23,9 +23,9 @@ import (
"github.com/apilayer/freegeoip" "github.com/apilayer/freegeoip"
) )
// GeoDBInfo contains all the geographical information we could extract based on an IP // geoDBInfo contains all the geographical information we could extract based on an IP
// address. // address.
type GeoDBInfo struct { type geoDBInfo struct {
Country struct { Country struct {
Names struct { Names struct {
English string `maxminddb:"en" json:"en,omitempty"` English string `maxminddb:"en" json:"en,omitempty"`
@ -42,22 +42,22 @@ type GeoDBInfo struct {
} `maxminddb:"location" json:"location,omitempty"` } `maxminddb:"location" json:"location,omitempty"`
} }
// GeoLocation contains geographical information. // geoLocation contains geographical information.
type GeoLocation struct { type geoLocation struct {
Country string `json:"country,omitempty"` Country string `json:"country,omitempty"`
City string `json:"city,omitempty"` City string `json:"city,omitempty"`
Latitude float64 `json:"latitude,omitempty"` Latitude float64 `json:"latitude,omitempty"`
Longitude float64 `json:"longitude,omitempty"` Longitude float64 `json:"longitude,omitempty"`
} }
// GeoDB represents a geoip database that can be queried for IP to geographical // geoDB represents a geoip database that can be queried for IP to geographical
// information conversions. // information conversions.
type GeoDB struct { type geoDB struct {
geodb *freegeoip.DB geodb *freegeoip.DB
} }
// Open creates a new geoip database with an up-to-date database from the internet. // Open creates a new geoip database with an up-to-date database from the internet.
func OpenGeoDB() (*GeoDB, error) { func openGeoDB() (*geoDB, error) {
// Initiate a geoip database to cross reference locations // Initiate a geoip database to cross reference locations
db, err := freegeoip.OpenURL(freegeoip.MaxMindDB, 24*time.Hour, time.Hour) db, err := freegeoip.OpenURL(freegeoip.MaxMindDB, 24*time.Hour, time.Hour)
if err != nil { if err != nil {
@ -70,26 +70,26 @@ func OpenGeoDB() (*GeoDB, error) {
return nil, err return nil, err
} }
// Assemble and return our custom wrapper // Assemble and return our custom wrapper
return &GeoDB{geodb: db}, nil return &geoDB{geodb: db}, nil
} }
// Close terminates the database background updater. // Close terminates the database background updater.
func (db *GeoDB) Close() error { func (db *geoDB) close() error {
db.geodb.Close() db.geodb.Close()
return nil return nil
} }
// Lookup converts an IP address to a geographical location. // Lookup converts an IP address to a geographical location.
func (db *GeoDB) Lookup(ip net.IP) *GeoDBInfo { func (db *geoDB) lookup(ip net.IP) *geoDBInfo {
result := new(GeoDBInfo) result := new(geoDBInfo)
db.geodb.Lookup(ip, result) db.geodb.Lookup(ip, result)
return result return result
} }
// Location retrieves the geographical location of the given IP address. // Location retrieves the geographical location of the given IP address.
func (db *GeoDB) Location(ip string) *GeoLocation { func (db *geoDB) location(ip string) *geoLocation {
location := db.Lookup(net.ParseIP(ip)) location := db.lookup(net.ParseIP(ip))
return &GeoLocation{ return &geoLocation{
Country: location.Country.Names.English, Country: location.Country.Names.English,
City: location.City.Names.English, City: location.City.Names.English,
Latitude: location.Location.Latitude, Latitude: location.Location.Latitude,

View file

@ -18,7 +18,6 @@ package dashboard
import ( import (
"encoding/json" "encoding/json"
"time"
) )
type Message struct { type Message struct {
@ -34,7 +33,6 @@ type Message struct {
type ChartEntries []*ChartEntry type ChartEntries []*ChartEntry
type ChartEntry struct { type ChartEntry struct {
Time time.Time `json:"time"`
Value float64 `json:"value"` Value float64 `json:"value"`
} }
@ -58,8 +56,8 @@ type TxPoolMessage struct {
// NetworkMessage contains information about the peers // NetworkMessage contains information about the peers
// organized based on their IP address and node ID. // organized based on their IP address and node ID.
type NetworkMessage struct { type NetworkMessage struct {
Peers *PeerContainer `json:"peers,omitempty"` // Peer tree. Peers *peerContainer `json:"peers,omitempty"` // Peer tree.
Diff []*PeerEvent `json:"diff,omitempty"` // Events that change the peer tree. Diff []*peerEvent `json:"diff,omitempty"` // Events that change the peer tree.
} }
// SystemMessage contains the metered system data samples. // SystemMessage contains the metered system data samples.

View file

@ -28,9 +28,9 @@ import (
) )
const ( const (
eventBufferLimit = 128 // Maximum number of events of buffered peer events. eventBufferLimit = 128 // Maximum number of buffered peer events.
knownPeerLimit = 30 //p2p.MeteredPeerLimit // Maximum number of stored peers, which successfully made the handshake. knownPeerLimit = 100 // Maximum number of stored peers, which successfully made the handshake.
unknownPeerLimit = 100 //p2p.MeteredPeerLimit // Maximum number of stored peers, which failed to make the handshake. attemptLimit = 100 // Maximum number of stored peers, which failed to make the handshake.
// eventLimit is the maximum number of the dashboard's custom peer events, // eventLimit is the maximum number of the dashboard's custom peer events,
// that are collected between two metering period and sent to the clients // that are collected between two metering period and sent to the clients
@ -39,16 +39,17 @@ const (
eventLimit = knownPeerLimit << 2 eventLimit = knownPeerLimit << 2
) )
// PeerContainer contains information about the node's peers. This data structure // peerContainer contains information about the node's peers. This data structure
// maintains the metered peer data based on the different behaviours of the peers. // maintains the metered peer data based on the different behaviours of the peers.
// //
// Every peer has an IP address, and the peers that manage to make the handshake // Every peer has an IP address, and the peers that manage to make the handshake
// (known peers) have node IDs too. There can appear more peers with the same IP, // (known peers) have node IDs too. There can appear more peers with the same IP,
// therefore the peer maintainer data structure is a tree consisting of a map of // therefore the peer container data structure is a tree consisting of a map of
// maps, where the first key groups the peers by IP, while the second one groups // maps, where the first key groups the peers by IP, while the second one groups
// them by the node ID. The peers failing before the handshake (unknown peers) // them by the node ID. The known peers can be active if their connection is still
// only have IP addresses, so their connection attempts are stored as part of the // open, or inactive otherwise. The peers failing before the handshake (unknown
// value of the outer map. // peers) only have IP addresses, so their connection attempts are stored as part
// of the value of the outer map.
// //
// Another criteria is to limit the number of metered peers so that // Another criteria is to limit the number of metered peers so that
// they don't fill the memory. The selection order is based on the // they don't fill the memory. The selection order is based on the
@ -61,12 +62,9 @@ const (
// the limit, the first element is removed from the list, as well as from // the limit, the first element is removed from the list, as well as from
// the tree. // the tree.
// //
// The active peers that are still connected have priority over the disconnected // The active peers have priority over the inactive ones, therefore
// ones, therefore the list is extended by a separator, which is a pointer to a // they have their own list. The separation makes it sure that the
// list element. The separator separates the active peers from the inactive ones, // inactive peers are always removed before the active ones.
// and it is the entry for the list. If the peer that is to be inserted is active,
// it goes after the separator, otherwise it goes before. This way the active peers
// never move to the front before the inactive ones.
// //
// The peers that don't manage to make handshake are not inserted into the list, // The peers that don't manage to make handshake are not inserted into the list,
// only their connection attempts are appended to the array belonging to their IP. // only their connection attempts are appended to the array belonging to their IP.
@ -76,116 +74,99 @@ const (
// //
// This data structure makes it possible to marshal the peer // This data structure makes it possible to marshal the peer
// history simply by passing it to the JSON marshaler. // history simply by passing it to the JSON marshaler.
type PeerContainer struct { type peerContainer struct {
// Bundles is the outer map using the peer's IP address as key. // Bundles is the outer map using the peer's IP address as key.
Bundles map[string]*PeerBundle `json:"bundles,omitempty"` Bundles map[string]*peerBundle `json:"bundles,omitempty"`
// activeSeparator is a pointer to the last inactive peer element, splitting // activePeers contains the peers with opened connection in random order.
// the list into an inactive and an active part, and forming the entry for activePeers *list.List
// the peer list.
activeSeparator *list.Element
// knownPeers contains the peers that managed to make handshake. // inactivePeers contains the peers with closed connection in chronological order.
knownPeers *list.List inactivePeers *list.List
// unknownPeers is the super array containing the IP addresses, from which // attemptOrder is the super array containing the IP addresses, from which
// the peers attempted to connect then failed before/during the handshake. // the peers attempted to connect then failed before/during the handshake.
// Its values are appended in chronological order, which means that the // Its values are appended in chronological order, which means that the
// oldest attempt is at the beginning of the array. When the first element // oldest attempt is at the beginning of the array. When the first element
// is removed, the first element of the related bundle's attempt array is // is removed, the first element of the related bundle's attempt array is
// removed too, ensuring that always the latest attempts are stored. // removed too, ensuring that always the latest attempts are stored.
unknownPeers []string attemptOrder []string
// geodb is the geoip database used to retrieve the peers' geographical location. // geodb is the geoip database used to retrieve the peers' geographical location.
geodb *GeoDB geodb *geoDB
// refresh is the refresh rate used to generate the
// initial auxiliary traffic samples' time stamps.
refresh time.Duration
} }
// NewPeerContainer returns a new instance of the peer container. // newPeerContainer returns a new instance of the peer container.
func NewPeerContainer(geodb *GeoDB, refresh time.Duration) *PeerContainer { func newPeerContainer(geodb *geoDB) *peerContainer {
return &PeerContainer{ return &peerContainer{
Bundles: make(map[string]*PeerBundle), Bundles: make(map[string]*peerBundle),
knownPeers: list.New(), activePeers: list.New(),
unknownPeers: make([]string, 0, unknownPeerLimit), inactivePeers: list.New(),
attemptOrder: make([]string, 0, attemptLimit),
geodb: geodb, geodb: geodb,
refresh: refresh,
} }
} }
// getOrInitBundle inserts a new peer bundle into the map, if the peer belonging // bundle inserts a new peer bundle into the map, if the peer belonging
// to the given IP wasn't metered so far. In this case retrieves the location of // to the given IP wasn't metered so far. In this case retrieves the location of
// the IP address from the database and creates a corresponding peer event. // the IP address from the database and creates a corresponding peer event.
// Returns the bundle belonging to the given IP and the events occurring during // Returns the bundle belonging to the given IP and the events occurring during
// the initialization. // the initialization.
func (pc *PeerContainer) getOrInitBundle(ip string) (*PeerBundle, []*PeerEvent) { func (pc *peerContainer) bundle(ip string) (*peerBundle, []*peerEvent) {
var events []*PeerEvent var events []*peerEvent
if _, ok := pc.Bundles[ip]; !ok { if _, ok := pc.Bundles[ip]; !ok {
location := pc.geodb.Location(ip) location := pc.geodb.location(ip)
events = append(events, &PeerEvent{ events = append(events, &peerEvent{
IP: ip, IP: ip,
Location: location, Location: location,
}) })
pc.Bundles[ip] = &PeerBundle{ pc.Bundles[ip] = &peerBundle{
Location: location, Location: location,
KnownPeers: make(map[string]*KnownPeer), KnownPeers: make(map[string]*knownPeer),
} }
} }
return pc.Bundles[ip], events return pc.Bundles[ip], events
} }
func (pc *PeerContainer) getOrInitKnownPeer(ip, id string) (*KnownPeer, []*PeerEvent) {
bundle, events := pc.getOrInitBundle(ip)
peer, peerEvents := bundle.getOrInitKnownPeer(ip, id, pc.refresh)
return peer, append(events, peerEvents...)
}
// extendKnown handles the events of the successfully connected peers. // extendKnown handles the events of the successfully connected peers.
// Returns the events occurring during the extension. // Returns the events occurring during the extension.
func (pc *PeerContainer) extendKnown(event *PeerEvent) []*PeerEvent { func (pc *peerContainer) extendKnown(event *peerEvent) []*peerEvent {
peer, events := pc.getOrInitKnownPeer(event.IP, event.ID) bundle, events := pc.bundle(event.IP)
peer, peerEvents := bundle.knownPeer(event.IP, event.ID)
events = append(events, peerEvents...)
// Append the connect and the disconnect events to // Append the connect and the disconnect events to
// the corresponding arrays keeping the limit. // the corresponding arrays keeping the limit.
if event.Connected != nil { switch {
case event.Connected != nil:
peer.Connected = append(peer.Connected, event.Connected) peer.Connected = append(peer.Connected, event.Connected)
if first := len(peer.Connected) - sampleLimit; first > 0 { if first := len(peer.Connected) - sampleLimit; first > 0 {
peer.Connected = peer.Connected[first:] peer.Connected = peer.Connected[first:]
} }
} case event.Disconnected != nil:
if event.Disconnected != nil {
peer.Disconnected = append(peer.Disconnected, event.Disconnected) peer.Disconnected = append(peer.Disconnected, event.Disconnected)
if first := len(peer.Disconnected) - sampleLimit; first > 0 { if first := len(peer.Disconnected) - sampleLimit; first > 0 {
peer.Disconnected = peer.Disconnected[first:] peer.Disconnected = peer.Disconnected[first:]
} }
} }
if peer.listElement != nil { if peer.listElement != nil {
if pc.activeSeparator == peer.listElement {
pc.activeSeparator = pc.activeSeparator.Prev()
}
// If the peer is already in the list, remove and reinsert it. // If the peer is already in the list, remove and reinsert it.
_ = pc.knownPeers.Remove(peer.listElement) _ = pc.activePeers.Remove(peer.listElement)
_ = pc.inactivePeers.Remove(peer.listElement)
peer.listElement = nil peer.listElement = nil
} }
// Insert the peer into the list. // Insert the peer into the list.
if pc.activeSeparator == nil { peer.listElement = pc.activePeers.PushBack(peer)
// If there isn't active peer in the list for pc.activePeers.Len()+pc.inactivePeers.Len() > knownPeerLimit {
peer.listElement = pc.knownPeers.PushBack(peer) // While the count of the known peers is greater than the limit,
pc.activeSeparator = peer.listElement // remove the first element from the inactive peer list and from the map.
} else if e := pc.knownPeers.InsertAfter(peer, pc.activeSeparator); e != nil { if removedPeer, ok := pc.inactivePeers.Remove(pc.inactivePeers.Front()).(*knownPeer); ok {
// Insert the peer after the last inactive peer, and set it as the separator. events = append(events, pc.removeKnown(removedPeer.ip, removedPeer.id)...)
peer.listElement, pc.activeSeparator = e, e
} else { } else {
log.Warn("Failed to insert known peer", "peer", *peer) log.Warn("Failed to parse the removed peer")
} }
for pc.knownPeers.Len() > knownPeerLimit {
// While the length of the list is greater than the limit,
// remove the first element from the list and from the map.
if pc.activeSeparator == pc.knownPeers.Front() {
pc.activeSeparator = nil
} }
if removedPeer, ok := pc.knownPeers.Remove(pc.knownPeers.Front()).(*KnownPeer); ok { for pc.activePeers.Len() > knownPeerLimit {
if removedPeer, ok := pc.activePeers.Remove(pc.activePeers.Front()).(*knownPeer); ok {
events = append(events, pc.removeKnown(removedPeer.ip, removedPeer.id)...) events = append(events, pc.removeKnown(removedPeer.ip, removedPeer.id)...)
} else { } else {
log.Warn("Failed to parse the removed peer") log.Warn("Failed to parse the removed peer")
@ -194,140 +175,125 @@ func (pc *PeerContainer) extendKnown(event *PeerEvent) []*PeerEvent {
return events return events
} }
// extendUnknown handles the events of the peers failing before/during the handshake. // handleAttempt handles the events of the peers failing before/during the handshake.
// Returns the events occurring during the extension. // Returns the events occurring during the extension.
func (pc *PeerContainer) extendUnknown(event *PeerEvent) []*PeerEvent { func (pc *peerContainer) handleAttempt(event *peerEvent) []*peerEvent {
bundle, events := pc.getOrInitBundle(event.IP) bundle, events := pc.bundle(event.IP)
bundle.UnknownPeers = append(bundle.UnknownPeers, &UnknownPeer{ bundle.Attempts = append(bundle.Attempts, &peerAttempt{
Connected: *event.Connected, Connected: *event.Connected,
Disconnected: *event.Disconnected, Disconnected: *event.Disconnected,
}) })
pc.unknownPeers = append(pc.unknownPeers, event.IP) pc.attemptOrder = append(pc.attemptOrder, event.IP)
for len(pc.unknownPeers) > unknownPeerLimit { for len(pc.attemptOrder) > attemptLimit {
// While the length of the connection attempt order array is greater // While the length of the connection attempt order array is greater
// than the limit, remove the first element from the involved peer's // than the limit, remove the first element from the involved peer's
// array and also from the super array. // array and also from the super array.
events = append(events, pc.removeUnknown(pc.unknownPeers[0])...) events = append(events, pc.removeAttempt(pc.attemptOrder[0])...)
pc.unknownPeers = pc.unknownPeers[1:] pc.attemptOrder = pc.attemptOrder[1:]
} }
return events return events
} }
// setActive moves the peer denoted by the given IP address and node ID after // setActive pushes the peer denoted by the given IP address and node ID
// the list's active separator. Takes no effect if the peer doesn't exist. // into the active peer list. Takes no effect if the peer doesn't exist.
func (pc *PeerContainer) setActive(ip, id string) { func (pc *peerContainer) setActive(ip, id string) {
if bundle, ok := pc.Bundles[ip]; ok { if bundle, ok := pc.Bundles[ip]; ok {
if peer, ok := bundle.KnownPeers[id]; ok { if peer, ok := bundle.KnownPeers[id]; ok {
if peer.listElement != nil { if peer.listElement != nil {
// If the peer is already in the list, remove it first. _ = pc.inactivePeers.Remove(peer.listElement)
// Theoretically this should always happen, because all _ = pc.activePeers.Remove(peer.listElement)
// the peers are inserted into the list.
if pc.activeSeparator == peer.listElement {
pc.activeSeparator = pc.activeSeparator.Prev()
}
_ = pc.knownPeers.Remove(peer.listElement)
}
if pc.activeSeparator == nil {
// If there isn't active peer yet.
peer.listElement = pc.knownPeers.PushBack(peer)
pc.activeSeparator = peer.listElement
} else if e := pc.knownPeers.InsertAfter(peer, pc.activeSeparator); e != nil {
peer.listElement = e
} else {
log.Warn("Failed to insert the peer after the separator", "peer", peer)
} }
peer.listElement = pc.activePeers.PushBack(peer)
} }
} }
} }
// resetActiveSeparator resets the active separator, denoting // resetActive pushes the active peers to the end of the inactive peer
// that active peers are not considered active anymore. // list, denoting that active peers are not considered active anymore.
func (pc *PeerContainer) resetActiveSeparator() { func (pc *peerContainer) resetActive() {
pc.activeSeparator = nil for pc.activePeers.Front() != nil {
pc.inactivePeers.PushBack(pc.activePeers.Remove(pc.activePeers.Front()))
}
} }
// PeerBundle contains the peers belonging to a given IP address. // peerBundle contains the peers belonging to a given IP address.
type PeerBundle struct { type peerBundle struct {
// Location contains the geographical location based on the bundle's IP address. // Location contains the geographical location based on the bundle's IP address.
Location *GeoLocation `json:"location,omitempty"` Location *geoLocation `json:"location,omitempty"`
// KnownPeers is the inner map of the metered peer // KnownPeers is the inner map of the metered peer
// maintainer data structure using the node ID as key. // maintainer data structure using the node ID as key.
KnownPeers map[string]*KnownPeer `json:"knownPeers,omitempty"` KnownPeers map[string]*knownPeer `json:"knownPeers,omitempty"`
// UnknownPeers contains the failed connection attempts of the // Attempts contains the failed connection attempts of the
// peers belonging to a given IP address in chronological order. // peers belonging to a given IP address in chronological order.
UnknownPeers []*UnknownPeer `json:"unknownPeers,omitempty"` Attempts []*peerAttempt `json:"attempts,omitempty"`
} }
// removeKnown removes the known peer belonging to the // removeKnown removes the known peer belonging to the
// given IP address and node ID from the peer tree. // given IP address and node ID from the peer tree.
func (pc *PeerContainer) removeKnown(ip, id string) (events []*PeerEvent) { func (pc *peerContainer) removeKnown(ip, id string) (events []*peerEvent) {
if bundle, ok := pc.Bundles[ip]; ok { if bundle, ok := pc.Bundles[ip]; ok {
if _, ok := bundle.KnownPeers[id]; ok { if _, ok := bundle.KnownPeers[id]; ok {
events = append(events, &PeerEvent{ events = append(events, &peerEvent{
Remove: RemoveKnown, Remove: RemoveKnown,
IP: ip, IP: ip,
ID: id, ID: id,
}) })
delete(bundle.KnownPeers, id) delete(bundle.KnownPeers, id)
} else { } else {
log.Warn("No peer to remove", ip, id) log.Warn("No peer to remove", "ip", ip, "id", id)
} }
if len(bundle.KnownPeers) < 1 && len(bundle.UnknownPeers) < 1 { if len(bundle.KnownPeers) < 1 && len(bundle.Attempts) < 1 {
events = append(events, &PeerEvent{ events = append(events, &peerEvent{
Remove: RemoveBundle, Remove: RemoveBundle,
IP: ip, IP: ip,
}) })
delete(pc.Bundles, ip) delete(pc.Bundles, ip)
} }
} else {
log.Warn("No bundle to remove", ip)
} }
return events return events
} }
// removeUnknown removes the unknown peer belonging to the // removeAttempt removes the peer attempt belonging to the
// given IP address and node ID from the peer tree. // given IP address and node ID from the peer tree.
func (pc *PeerContainer) removeUnknown(ip string) (events []*PeerEvent) { func (pc *peerContainer) removeAttempt(ip string) (events []*peerEvent) {
if bundle, ok := pc.Bundles[ip]; ok { if bundle, ok := pc.Bundles[ip]; ok {
if len(bundle.UnknownPeers) > 0 { if len(bundle.Attempts) > 0 {
events = append(events, &PeerEvent{ events = append(events, &peerEvent{
Remove: RemoveUnknown, Remove: RemoveAttempt,
IP: ip, IP: ip,
}) })
bundle.UnknownPeers = bundle.UnknownPeers[1:] bundle.Attempts = bundle.Attempts[1:]
} }
if len(bundle.UnknownPeers) < 1 && len(bundle.KnownPeers) < 1 { if len(bundle.Attempts) < 1 && len(bundle.KnownPeers) < 1 {
events = append(events, &PeerEvent{ events = append(events, &peerEvent{
Remove: RemoveBundle, Remove: RemoveBundle,
IP: ip, IP: ip,
}) })
delete(pc.Bundles, ip) delete(pc.Bundles, ip)
} }
} else {
log.Warn("No bundle to remove", ip)
} }
return events return events
} }
// getOrInitKnownPeer inserts a new peer into the map, if the peer belonging // knownPeer inserts a new peer into the map, if the peer belonging
// to the given IP address and node ID wasn't metered so far. Returns the peer // to the given IP address and node ID wasn't metered so far. Returns the peer
// belonging to the given IP and ID as well as the events occurring during the // belonging to the given IP and ID as well as the events occurring during the
// initialization. // initialization.
func (bundle *PeerBundle) getOrInitKnownPeer(ip, id string, refresh time.Duration) (*KnownPeer, []*PeerEvent) { func (bundle *peerBundle) knownPeer(ip, id string) (*knownPeer, []*peerEvent) {
var events []*PeerEvent var events []*peerEvent
if _, ok := bundle.KnownPeers[id]; !ok { if _, ok := bundle.KnownPeers[id]; !ok {
now := time.Now() now := time.Now()
ingress := emptyChartEntries(now, sampleLimit, refresh) ingress := emptyChartEntries(now, sampleLimit)
egress := emptyChartEntries(now, sampleLimit, refresh) egress := emptyChartEntries(now, sampleLimit)
events = append(events, &PeerEvent{ events = append(events, &peerEvent{
IP: ip, IP: ip,
ID: id, ID: id,
Ingress: append([]*ChartEntry{}, ingress...), Ingress: append([]*ChartEntry{}, ingress...),
Egress: append([]*ChartEntry{}, egress...), Egress: append([]*ChartEntry{}, egress...),
}) })
bundle.KnownPeers[id] = &KnownPeer{ bundle.KnownPeers[id] = &knownPeer{
ip: ip, ip: ip,
id: id, id: id,
Ingress: ingress, Ingress: ingress,
@ -337,8 +303,8 @@ func (bundle *PeerBundle) getOrInitKnownPeer(ip, id string, refresh time.Duratio
return bundle.KnownPeers[id], events return bundle.KnownPeers[id], events
} }
// KnownPeer contains the metered data of a particular peer. // knownPeer contains the metered data of a particular peer.
type KnownPeer struct { type knownPeer struct {
// Connected contains the timestamps of the peer's connection events. // Connected contains the timestamps of the peer's connection events.
Connected []*time.Time `json:"connected,omitempty"` Connected []*time.Time `json:"connected,omitempty"`
@ -361,8 +327,8 @@ type KnownPeer struct {
prevEgress float64 prevEgress float64
} }
// UnknownPeer contains a failed peer connection attempt's attributes. // peerAttempt contains a failed peer connection attempt's attributes.
type UnknownPeer struct { type peerAttempt struct {
// Connected contains the timestamp of the connection attempt's moment. // Connected contains the timestamp of the connection attempt's moment.
Connected time.Time `json:"connected"` Connected time.Time `json:"connected"`
@ -375,25 +341,27 @@ type RemovedPeerType string
const ( const (
RemoveKnown RemovedPeerType = "known" RemoveKnown RemovedPeerType = "known"
RemoveUnknown RemovedPeerType = "unknown" RemoveAttempt RemovedPeerType = "attempt"
RemoveBundle RemovedPeerType = "bundle" RemoveBundle RemovedPeerType = "bundle"
) )
// PeerEvent contains the attributes of a peer event. // peerEvent contains the attributes of a peer event.
type PeerEvent struct { type peerEvent struct {
IP string `json:"ip,omitempty"` // IP address of the peer. IP string `json:"ip,omitempty"` // IP address of the peer.
ID string `json:"id,omitempty"` // Node ID of the peer. ID string `json:"id,omitempty"` // Node ID of the peer.
Remove RemovedPeerType `json:"remove,omitempty"` // Type of the peer that is to be removed. Remove RemovedPeerType `json:"remove,omitempty"` // Type of the peer that is to be removed.
Location *GeoLocation `json:"location,omitempty"` // Geographical location of the peer. Location *geoLocation `json:"location,omitempty"` // Geographical location of the peer.
Connected *time.Time `json:"connected,omitempty"` // Timestamp of the connection moment. Connected *time.Time `json:"connected,omitempty"` // Timestamp of the connection moment.
Disconnected *time.Time `json:"disconnected,omitempty"` // Timestamp of the disonnection moment. Disconnected *time.Time `json:"disconnected,omitempty"` // Timestamp of the disonnection moment.
Ingress ChartEntries `json:"ingress,omitempty"` // Ingress samples. Ingress ChartEntries `json:"ingress,omitempty"` // Ingress samples.
Egress ChartEntries `json:"egress,omitempty"` // Egress samples. Egress ChartEntries `json:"egress,omitempty"` // Egress samples.
} }
// trafficMap // trafficMap is a container for the periodically collected peer traffic.
type trafficMap map[string]map[string]float64 type trafficMap map[string]map[string]float64
// insert inserts a new value to the traffic map. Overwrites
// the value at the given ip and id if that already exists.
func (m *trafficMap) insert(ip, id string, val float64) { func (m *trafficMap) insert(ip, id string, val float64) {
if _, ok := (*m)[ip]; !ok { if _, ok := (*m)[ip]; !ok {
(*m)[ip] = make(map[string]float64) (*m)[ip] = make(map[string]float64)
@ -407,12 +375,12 @@ func (db *Dashboard) collectPeerData() {
// Open the geodb database for IP to geographical information conversions. // Open the geodb database for IP to geographical information conversions.
var err error var err error
db.geodb, err = OpenGeoDB() db.geodb, err = openGeoDB()
if err != nil { if err != nil {
log.Warn("Failed to open geodb", "err", err) log.Warn("Failed to open geodb", "err", err)
return return
} }
defer db.geodb.Close() defer db.geodb.close()
peerCh := make(chan p2p.MeteredPeerEvent, eventBufferLimit) // Peer event channel. peerCh := make(chan p2p.MeteredPeerEvent, eventBufferLimit) // Peer event channel.
subPeer := p2p.SubscribeMeteredPeerEvent(peerCh) // Subscribe to peer events. subPeer := p2p.SubscribeMeteredPeerEvent(peerCh) // Subscribe to peer events.
@ -449,7 +417,7 @@ func (db *Dashboard) collectPeerData() {
collectIngress := trafficCollector(p2p.MetricsInboundTraffic + "/") collectIngress := trafficCollector(p2p.MetricsInboundTraffic + "/")
collectEgress := trafficCollector(p2p.MetricsOutboundTraffic + "/") collectEgress := trafficCollector(p2p.MetricsOutboundTraffic + "/")
peers := NewPeerContainer(db.geodb, db.config.Refresh) peers := newPeerContainer(db.geodb)
db.peerLock.Lock() db.peerLock.Lock()
db.history.Network = &NetworkMessage{ db.history.Network = &NetworkMessage{
Peers: peers, Peers: peers,
@ -458,7 +426,7 @@ func (db *Dashboard) collectPeerData() {
// newPeerEvents contains peer events, which trigger operations that // newPeerEvents contains peer events, which trigger operations that
// will be executed on the peer tree after a metering period. // will be executed on the peer tree after a metering period.
newPeerEvents := make([]*PeerEvent, 0, eventLimit) newPeerEvents := make([]*peerEvent, 0, eventLimit)
ingress, egress := new(trafficMap), new(trafficMap) ingress, egress := new(trafficMap), new(trafficMap)
*ingress, *egress = make(trafficMap), make(trafficMap) *ingress, *egress = make(trafficMap), make(trafficMap)
@ -469,14 +437,14 @@ func (db *Dashboard) collectPeerData() {
switch event.Type { switch event.Type {
case p2p.PeerConnected: case p2p.PeerConnected:
connected := now.Add(-event.Elapsed) connected := now.Add(-event.Elapsed)
newPeerEvents = append(newPeerEvents, &PeerEvent{ newPeerEvents = append(newPeerEvents, &peerEvent{
IP: event.IP.String(), IP: event.IP.String(),
ID: event.ID.String(), ID: event.ID.String(),
Connected: &connected, Connected: &connected,
}) })
case p2p.PeerDisconnected: case p2p.PeerDisconnected:
ip, id := event.IP.String(), event.ID.String() ip, id := event.IP.String(), event.ID.String()
newPeerEvents = append(newPeerEvents, &PeerEvent{ newPeerEvents = append(newPeerEvents, &peerEvent{
IP: ip, IP: ip,
ID: id, ID: id,
Disconnected: &now, Disconnected: &now,
@ -491,7 +459,7 @@ func (db *Dashboard) collectPeerData() {
egress.insert(ip, id, float64(event.Egress)) egress.insert(ip, id, float64(event.Egress))
case p2p.PeerHandshakeFailed: case p2p.PeerHandshakeFailed:
connected := now.Add(-event.Elapsed) connected := now.Add(-event.Elapsed)
newPeerEvents = append(newPeerEvents, &PeerEvent{ newPeerEvents = append(newPeerEvents, &peerEvent{
IP: event.IP.String(), IP: event.IP.String(),
Connected: &connected, Connected: &connected,
Disconnected: &now, Disconnected: &now,
@ -517,9 +485,9 @@ func (db *Dashboard) collectPeerData() {
// the diff, otherwise the active peers can be removed. // the diff, otherwise the active peers can be removed.
// //
// After a metering period the active peers can become inactive, // After a metering period the active peers can become inactive,
// so resetting the separator makes it sure, that only the active // so at the beginning it is necessary to transpose them to the
// peers move to the protected part of the list. // inactive list.
peers.resetActiveSeparator() peers.resetActive()
for ip, bundle := range *ingress { for ip, bundle := range *ingress {
for id := range bundle { for id := range bundle {
// Only set the peers that are inserted both // Only set the peers that are inserted both
@ -531,7 +499,7 @@ func (db *Dashboard) collectPeerData() {
} }
} }
} }
var diff []*PeerEvent var diff []*peerEvent
for i := 0; i < len(newPeerEvents); i++ { for i := 0; i < len(newPeerEvents); i++ {
if newPeerEvents[i].IP == "" { if newPeerEvents[i].IP == "" {
log.Warn("Peer event without IP", "event", *newPeerEvents[i]) log.Warn("Peer event without IP", "event", *newPeerEvents[i])
@ -541,18 +509,16 @@ func (db *Dashboard) collectPeerData() {
// There are two main branches of peer events coming from the event // There are two main branches of peer events coming from the event
// feed, one belongs to the known peers, one to the unknown peers. // feed, one belongs to the known peers, one to the unknown peers.
// If the event has node ID, it belongs to a known peer, otherwise // If the event has node ID, it belongs to a known peer, otherwise
// to an unknown one. // to an unknown one, which is considered as connection attempt.
// //
// The extension can produce additional peer events, such // The extension can produce additional peer events, such
// as remove, location and initial samples events. // as remove, location and initial samples events.
if newPeerEvents[i].ID == "" { if newPeerEvents[i].ID == "" {
diff = append(diff, peers.extendUnknown(newPeerEvents[i])...) diff = append(diff, peers.handleAttempt(newPeerEvents[i])...)
continue continue
} }
diff = append(diff, peers.extendKnown(newPeerEvents[i])...) diff = append(diff, peers.extendKnown(newPeerEvents[i])...)
} }
now := time.Now()
// Update the peer tree using the traffic maps. // Update the peer tree using the traffic maps.
for ip, bundle := range peers.Bundles { for ip, bundle := range peers.Bundles {
for id, peer := range bundle.KnownPeers { for id, peer := range bundle.KnownPeers {
@ -568,11 +534,9 @@ func (db *Dashboard) collectPeerData() {
} }
peer.prevIngress, peer.prevEgress = curIngress, curEgress peer.prevIngress, peer.prevEgress = curIngress, curEgress
i := &ChartEntry{ i := &ChartEntry{
Time: now,
Value: deltaIngress, Value: deltaIngress,
} }
e := &ChartEntry{ e := &ChartEntry{
Time: now,
Value: deltaEgress, Value: deltaEgress,
} }
peer.Ingress = append(peer.Ingress, i) peer.Ingress = append(peer.Ingress, i)
@ -584,7 +548,7 @@ func (db *Dashboard) collectPeerData() {
peer.Egress = peer.Egress[first:] peer.Egress = peer.Egress[first:]
} }
// Creating the traffic sample events. // Creating the traffic sample events.
diff = append(diff, &PeerEvent{ diff = append(diff, &peerEvent{
IP: ip, IP: ip,
ID: id, ID: id,
Ingress: ChartEntries{i}, Ingress: ChartEntries{i},
@ -592,13 +556,8 @@ func (db *Dashboard) collectPeerData() {
}) })
} }
} }
//ss, _ := json.MarshalIndent(db.history.Network, "", " ")
//fmt.Println(string(ss))
db.peerLock.Unlock() db.peerLock.Unlock()
//s, _ := json.MarshalIndent(diff, "", " ")
//fmt.Println(string(s))
db.sendToAll(&Message{Network: &NetworkMessage{ db.sendToAll(&Message{Network: &NetworkMessage{
Diff: diff, Diff: diff,
}}) }})

View file

@ -92,39 +92,29 @@ func (db *Dashboard) collectSystemData() {
prevDiskRead = curDiskRead prevDiskRead = curDiskRead
prevDiskWrite = curDiskWrite prevDiskWrite = curDiskWrite
now := time.Now()
runtime.ReadMemStats(&mem) runtime.ReadMemStats(&mem)
activeMemory := &ChartEntry{ activeMemory := &ChartEntry{
Time: now,
Value: float64(mem.Alloc) / frequency, Value: float64(mem.Alloc) / frequency,
} }
virtualMemory := &ChartEntry{ virtualMemory := &ChartEntry{
Time: now,
Value: float64(mem.Sys) / frequency, Value: float64(mem.Sys) / frequency,
} }
networkIngress := &ChartEntry{ networkIngress := &ChartEntry{
Time: now,
Value: deltaNetworkIngress / frequency, Value: deltaNetworkIngress / frequency,
} }
networkEgress := &ChartEntry{ networkEgress := &ChartEntry{
Time: now,
Value: deltaNetworkEgress / frequency, Value: deltaNetworkEgress / frequency,
} }
processCPU := &ChartEntry{ processCPU := &ChartEntry{
Time: now,
Value: deltaProcessCPUTime / frequency / numCPU * 100, Value: deltaProcessCPUTime / frequency / numCPU * 100,
} }
systemCPU := &ChartEntry{ systemCPU := &ChartEntry{
Time: now,
Value: float64(deltaSystemCPUUsage.Sys+deltaSystemCPUUsage.User) / frequency / numCPU, Value: float64(deltaSystemCPUUsage.Sys+deltaSystemCPUUsage.User) / frequency / numCPU,
} }
diskRead := &ChartEntry{ diskRead := &ChartEntry{
Time: now,
Value: float64(deltaDiskRead) / frequency, Value: float64(deltaDiskRead) / frequency,
} }
diskWrite := &ChartEntry{ diskWrite := &ChartEntry{
Time: now,
Value: float64(deltaDiskWrite) / frequency, Value: float64(deltaDiskWrite) / frequency,
} }
db.sysLock.Lock() db.sysLock.Lock()

View file

@ -161,6 +161,7 @@ func (c *meteredConn) Write(b []byte) (n int, err error) {
// the ingress and the egress traffic registries using the peer's IP and node ID, // the ingress and the egress traffic registries using the peer's IP and node ID,
// also emits connect event. // also emits connect event.
func (c *meteredConn) handshakeDone(id enode.ID) { func (c *meteredConn) handshakeDone(id enode.ID) {
// TODO (kurkomisi): use the node URL instead of the pure node ID. (the String() method of *Node)
if atomic.AddInt32(&meteredPeerCount, 1) >= MeteredPeerLimit { if atomic.AddInt32(&meteredPeerCount, 1) >= MeteredPeerLimit {
// Don't register the peer in the traffic registries. // Don't register the peer in the traffic registries.
atomic.AddInt32(&meteredPeerCount, -1) atomic.AddInt32(&meteredPeerCount, -1)