diff --git a/.gitignore b/.gitignore index 3b762af..f293033 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # See https://help.github.com/ignore-files/ for more about ignoring files. +htdocs # dependencies node_modules diff --git a/package.json b/package.json index fccbd61..a4b7cde 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,9 @@ "packages/*" ], "scripts": { + "deploy": "scripts/deploy.sh", "start:frontend": "scripts/start-frontend.sh", + "build:frontend": "scripts/build-frontend.sh", "start:backend": "scripts/start-backend.sh", "build:backend": "scripts/build-backend.sh", "check:backend": "tsc -p packages/backend --noEmit", diff --git a/packages/backend/src/Chain.ts b/packages/backend/src/Chain.ts index bc95389..c5c0bf2 100644 --- a/packages/backend/src/Chain.ts +++ b/packages/backend/src/Chain.ts @@ -2,7 +2,7 @@ import * as EventEmitter from 'events'; import Node from './Node'; import Feed from './Feed'; import FeedSet from './FeedSet'; -import { Maybe, Types, FeedMessage, blockAverage } from '@dotstats/common'; +import { Maybe, Types, FeedMessage, NumStats } from '@dotstats/common'; const BLOCK_TIME_HISTORY = 10; @@ -16,7 +16,7 @@ export default class Chain { public height = 0 as Types.BlockNumber; public blockTimestamp = 0 as Types.Timestamp; - private blockTimes: Array = new Array(BLOCK_TIME_HISTORY).fill(0); + private blockTimes = new NumStats(BLOCK_TIME_HISTORY); private averageBlockTime: Maybe = null; constructor(label: Types.ChainLabel) { @@ -110,9 +110,9 @@ export default class Chain { } private updateAverageBlockTime(height: Types.BlockNumber, now: Types.Timestamp) { - this.blockTimes[height % BLOCK_TIME_HISTORY] = now - this.blockTimestamp; + this.blockTimes.push((now - this.blockTimestamp) as Types.Milliseconds); // We are guaranteed that count > 0 - this.averageBlockTime = blockAverage(this.blockTimes); + this.averageBlockTime = this.blockTimes.average(); } } diff --git a/packages/backend/src/Node.ts b/packages/backend/src/Node.ts index ec12dd8..e725a2f 100644 --- a/packages/backend/src/Node.ts +++ b/packages/backend/src/Node.ts @@ -1,7 +1,7 @@ import * as WebSocket from 'ws'; import * as EventEmitter from 'events'; -import { noop, timestamp, Maybe, Types, blockAverage } from '@dotstats/common'; +import { noop, timestamp, Maybe, Types, NumStats } from '@dotstats/common'; import { parseMessage, getBestBlock, Message, BestBlock, SystemInterval } from './message'; import { locate, Location } from './location'; import { getId, refreshId } from './nodeId'; @@ -42,7 +42,7 @@ export default class Node { private readonly ip: string; private readonly socket: WebSocket; - private blockTimes: Array = new Array(BLOCK_TIME_HISTORY); + private blockTimes = new NumStats(BLOCK_TIME_HISTORY); private lastBlockAt: Maybe = null; private pingStart = 0 as Types.Timestamp; private throttle = false; @@ -189,7 +189,7 @@ export default class Node { } public get average(): Types.Milliseconds { - return blockAverage(this.blockTimes); + return this.blockTimes.average(); } public get localBlockAt(): Types.Milliseconds { @@ -265,7 +265,7 @@ export default class Node { this.height = height; this.blockTimestamp = timestamp(); this.lastBlockAt = time; - this.blockTimes[height % BLOCK_TIME_HISTORY] = blockTime; + this.blockTimes.push(blockTime); this.blockTime = blockTime; if (blockTime > 100) { diff --git a/packages/common/src/block.ts b/packages/common/src/block.ts deleted file mode 100644 index c48fc32..0000000 --- a/packages/common/src/block.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Milliseconds } from './types'; - -export function blockAverage(blockTimes: Array): Milliseconds { - let count = 0; - let sum = 0; - - for (const time of blockTimes) { - if (time) { - count += 1; - sum += time; - } - } - - if (count === 0) { - return 0 as Milliseconds; - } - - return (sum / count) as Milliseconds; -} diff --git a/packages/common/src/helpers.ts b/packages/common/src/helpers.ts index 00c76d3..23bedde 100644 --- a/packages/common/src/helpers.ts +++ b/packages/common/src/helpers.ts @@ -35,3 +35,80 @@ export function sleep(time: Milliseconds): Promise { export const timestamp = Date.now as () => Timestamp; export function noop() {} + +/** + * Keep track of last N numbers pushed onto internal stack. + * Provides means to get an average of said numbers. + */ +export class NumStats { + private readonly stack: Array; + private readonly history: number; + private index: 0; + + constructor(history: number) { + if (history < 1) { + throw new Error('Must track at least one number'); + } + + this.history = history; + this.stack = new Array(history); + } + + public push(val: T) { + this.stack[this.index++ % this.history] = val; + } + + /** + * Get average value of all values on the stack. + * + * @return {T} average value + */ + public average(): T { + if (this.index === 0) { + return 0 as T; + } + + const list = this.nonEmpty(); + let sum = 0; + + for (const n of list as Array) { + sum += n; + } + + return (sum / list.length) as T; + } + + /** + * Get average value of all values of the stack after filtering + * out a number of highest and lowest values + * + * @param {number} extremes number of high/low values to ignore + * @return {T} average value + */ + public averageWithoutExtremes(extremes: number): T { + if (this.index === 0) { + return 0 as T; + } + + const list = this.nonEmpty(); + const count = list.length - extremes * 2; + + if (count < 1) { + // Not enough entries to remove desired number of extremes, + // fall back to regular average + return this.average(); + } + + let sum = 0; + + for (const n of list.sort((a, b) => a - b).slice(extremes, -extremes)) { + sum += n; + } + + return (sum / count) as T; + } + + private nonEmpty(): Readonly> { + return this.index < this.history ? this.stack.slice(0, this.index) : this.stack; + } +} diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 7101264..bcfe9ea 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -1,6 +1,5 @@ export * from './helpers'; export * from './id'; -export * from './block'; export * from './stringify'; import * as Types from './types'; @@ -9,4 +8,4 @@ import * as FeedMessage from './feed'; export { Types, FeedMessage }; // Increment this if breaking changes were made to types in `feed.ts` -export const VERSION: Types.FeedVersion = 13 as Types.FeedVersion; +export const VERSION: Types.FeedVersion = 14 as Types.FeedVersion; diff --git a/scripts/build-frontend.sh b/scripts/build-frontend.sh new file mode 100755 index 0000000..cc1c067 --- /dev/null +++ b/scripts/build-frontend.sh @@ -0,0 +1 @@ +cd ./packages/frontend && yarn run build && cp -r ./build ../../htdocs && cd ../../ diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100755 index 0000000..c60dee5 --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1 @@ +yarn run build:common && yarn run build:backend && yarn run build:frontend && pm2 restart all