summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2018-06-09 04:14:26 +0900
committersyuilo <syuilotan@yahoo.co.jp>2018-06-09 04:14:26 +0900
commitc78945436e39121f46e083eac7a6572ca2efe2d2 (patch)
treeb26036a9f38bda54b865e704f8bcd477f2caac0e /src
parentサーバーの統計情報をメモリに記憶するようにするなど (diff)
downloadsharkey-c78945436e39121f46e083eac7a6572ca2efe2d2.tar.gz
sharkey-c78945436e39121f46e083eac7a6572ca2efe2d2.tar.bz2
sharkey-c78945436e39121f46e083eac7a6572ca2efe2d2.zip
#1686
Diffstat (limited to 'src')
-rw-r--r--src/client/app/common/scripts/streaming/notes-stats.ts (renamed from src/client/app/common/scripts/streaming/server.ts)10
-rw-r--r--src/client/app/common/scripts/streaming/server-stats.ts30
-rw-r--r--src/client/app/common/views/widgets/index.ts2
-rw-r--r--src/client/app/common/views/widgets/posts-monitor.vue182
-rw-r--r--src/client/app/common/views/widgets/server.vue6
-rw-r--r--src/client/app/desktop/views/components/home.vue1
-rw-r--r--src/client/app/desktop/views/pages/deck/deck.widgets-column.vue1
-rw-r--r--src/client/app/mios.ts12
-rw-r--r--src/client/app/mobile/views/pages/widgets.vue1
-rw-r--r--src/db/mongodb.ts5
-rw-r--r--src/index.ts2
-rw-r--r--src/models/note.ts3
-rw-r--r--src/notes-stats-child.ts20
-rw-r--r--src/notes-stats.ts20
-rw-r--r--src/server-stats.ts2
-rw-r--r--src/server/api/stream/notes-stats.ts35
-rw-r--r--src/server/api/stream/server-stats.ts (renamed from src/server/api/stream/server.ts)0
-rw-r--r--src/server/api/streaming.ts12
18 files changed, 327 insertions, 17 deletions
diff --git a/src/client/app/common/scripts/streaming/server.ts b/src/client/app/common/scripts/streaming/notes-stats.ts
index 2ea4239288..9e3e78a709 100644
--- a/src/client/app/common/scripts/streaming/server.ts
+++ b/src/client/app/common/scripts/streaming/notes-stats.ts
@@ -3,15 +3,15 @@ import StreamManager from './stream-manager';
import MiOS from '../../../mios';
/**
- * Server stream connection
+ * Notes stats stream connection
*/
-export class ServerStream extends Stream {
+export class NotesStatsStream extends Stream {
constructor(os: MiOS) {
- super(os, 'server');
+ super(os, 'notes-stats');
}
}
-export class ServerStreamManager extends StreamManager<ServerStream> {
+export class NotesStatsStreamManager extends StreamManager<NotesStatsStream> {
private os: MiOS;
constructor(os: MiOS) {
@@ -22,7 +22,7 @@ export class ServerStreamManager extends StreamManager<ServerStream> {
public getConnection() {
if (this.connection == null) {
- this.connection = new ServerStream(this.os);
+ this.connection = new NotesStatsStream(this.os);
}
return this.connection;
diff --git a/src/client/app/common/scripts/streaming/server-stats.ts b/src/client/app/common/scripts/streaming/server-stats.ts
new file mode 100644
index 0000000000..9983dfcaf0
--- /dev/null
+++ b/src/client/app/common/scripts/streaming/server-stats.ts
@@ -0,0 +1,30 @@
+import Stream from './stream';
+import StreamManager from './stream-manager';
+import MiOS from '../../../mios';
+
+/**
+ * Server stats stream connection
+ */
+export class ServerStatsStream extends Stream {
+ constructor(os: MiOS) {
+ super(os, 'server-stats');
+ }
+}
+
+export class ServerStatsStreamManager extends StreamManager<ServerStatsStream> {
+ private os: MiOS;
+
+ constructor(os: MiOS) {
+ super();
+
+ this.os = os;
+ }
+
+ public getConnection() {
+ if (this.connection == null) {
+ this.connection = new ServerStatsStream(this.os);
+ }
+
+ return this.connection;
+ }
+}
diff --git a/src/client/app/common/views/widgets/index.ts b/src/client/app/common/views/widgets/index.ts
index a4cabc43ba..0190393ba7 100644
--- a/src/client/app/common/views/widgets/index.ts
+++ b/src/client/app/common/views/widgets/index.ts
@@ -4,6 +4,7 @@ import wAnalogClock from './analog-clock.vue';
import wVersion from './version.vue';
import wRss from './rss.vue';
import wServer from './server.vue';
+import wPostsMonitor from './posts-monitor.vue';
import wMemo from './memo.vue';
import wBroadcast from './broadcast.vue';
import wCalendar from './calendar.vue';
@@ -22,6 +23,7 @@ Vue.component('mkw-tips', wTips);
Vue.component('mkw-donation', wDonation);
Vue.component('mkw-broadcast', wBroadcast);
Vue.component('mkw-server', wServer);
+Vue.component('mkw-posts-monitor', wPostsMonitor);
Vue.component('mkw-memo', wMemo);
Vue.component('mkw-rss', wRss);
Vue.component('mkw-version', wVersion);
diff --git a/src/client/app/common/views/widgets/posts-monitor.vue b/src/client/app/common/views/widgets/posts-monitor.vue
new file mode 100644
index 0000000000..bdf811bad0
--- /dev/null
+++ b/src/client/app/common/views/widgets/posts-monitor.vue
@@ -0,0 +1,182 @@
+<template>
+<div class="mkw-posts-monitor">
+ <mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
+ <template slot="header">%fa:chart-line%%i18n:@title%</template>
+ <button slot="func" @click="toggle" title="%i18n:@toggle%">%fa:sort%</button>
+
+ <div class="qpdmibaztplkylerhdbllwcokyrfxeyj" :class="{ dual: props.view == 0 }" :data-darkmode="$store.state.device.darkmode">
+ <svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" preserveAspectRatio="none" v-show="props.view != 2">
+ <defs>
+ <linearGradient :id="fediGradientId" x1="0" x2="0" y1="1" y2="0">
+ <stop offset="0%" stop-color="hsl(200, 80%, 70%)"></stop>
+ <stop offset="100%" stop-color="hsl(90, 80%, 70%)"></stop>
+ </linearGradient>
+ <mask :id="fediMaskId" x="0" y="0" :width="viewBoxX" :height="viewBoxY">
+ <polyline
+ :points="fediPolylinePoints"
+ fill="none"
+ stroke="#fff"
+ stroke-width="1"/>
+ </mask>
+ </defs>
+ <rect
+ x="-1" y="-1"
+ :width="viewBoxX + 2" :height="viewBoxY + 2"
+ :style="`stroke: none; fill: url(#${ fediGradientId }); mask: url(#${ fediMaskId })`"/>
+ <text x="1" y="5">Fedi</text>
+ </svg>
+ <svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" preserveAspectRatio="none" v-show="props.view != 1">
+ <defs>
+ <linearGradient :id="localGradientId" x1="0" x2="0" y1="1" y2="0">
+ <stop offset="0%" stop-color="hsl(200, 80%, 70%)"></stop>
+ <stop offset="100%" stop-color="hsl(90, 80%, 70%)"></stop>
+ </linearGradient>
+ <mask :id="localMaskId" x="0" y="0" :width="viewBoxX" :height="viewBoxY">
+ <polyline
+ :points="localPolylinePoints"
+ fill="none"
+ stroke="#fff"
+ stroke-width="1"/>
+ </mask>
+ </defs>
+ <rect
+ x="-1" y="-1"
+ :width="viewBoxX + 2" :height="viewBoxY + 2"
+ :style="`stroke: none; fill: url(#${ localGradientId }); mask: url(#${ localMaskId })`"/>
+ <text x="1" y="5">Local</text>
+ </svg>
+ </div>
+ </mk-widget-container>
+</div>
+</template>
+
+<script lang="ts">
+import define from '../../../common/define-widget';
+import * as uuid from 'uuid';
+
+export default define({
+ name: 'server',
+ props: () => ({
+ design: 0,
+ view: 0
+ })
+}).extend({
+ data() {
+ return {
+ connection: null,
+ connectionId: null,
+ viewBoxY: 30,
+ stats: [],
+ fediGradientId: uuid(),
+ fediMaskId: uuid(),
+ localGradientId: uuid(),
+ localMaskId: uuid(),
+ fediPolylinePoints: '',
+ localPolylinePoints: '',
+ fediPolygonPoints: '',
+ localPolygonPoints: ''
+ };
+ },
+ computed: {
+ viewBoxX(): number {
+ return this.props.view == 0 ? 50 : 100;
+ }
+ },
+ watch: {
+ viewBoxX() {
+ this.draw();
+ }
+ },
+ mounted() {
+ this.connection = (this as any).os.streams.notesStatsStream.getConnection();
+ this.connectionId = (this as any).os.streams.notesStatsStream.use();
+
+ this.connection.on('stats', this.onStats);
+ this.connection.on('statsLog', this.onStatsLog);
+ this.connection.send({
+ type: 'requestLog',
+ id: Math.random().toString()
+ });
+ },
+ beforeDestroy() {
+ this.connection.off('stats', this.onStats);
+ this.connection.off('statsLog', this.onStatsLog);
+ (this as any).os.streams.notesStatsStream.dispose(this.connectionId);
+ },
+ methods: {
+ toggle() {
+ if (this.props.view == 2) {
+ this.props.view = 0;
+ } else {
+ this.props.view++;
+ }
+ this.save();
+ },
+ func() {
+ if (this.props.design == 2) {
+ this.props.design = 0;
+ } else {
+ this.props.design++;
+ }
+ this.save();
+ },
+ draw() {
+ const stats = this.props.view == 0 ? this.stats.slice(0, 50) : this.stats;
+ const fediPeak = Math.max.apply(null, this.stats.map(x => x.all)) || 1;
+ const localPeak = Math.max.apply(null, this.stats.map(x => x.local)) || 1;
+
+ this.fediPolylinePoints = this.stats.map((s, i) => `${this.viewBoxX - ((this.stats.length - 1) - i)},${(1 - (s.all / fediPeak)) * this.viewBoxY}`).join(' ');
+ this.localPolylinePoints = this.stats.map((s, i) => `${this.viewBoxX - ((this.stats.length - 1) - i)},${(1 - (s.local / localPeak)) * this.viewBoxY}`).join(' ');
+
+ this.fediPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${ this.viewBoxY } ${ this.fediPolylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`;
+ this.localPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${ this.viewBoxY } ${ this.localPolylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`;
+ },
+ onStats(stats) {
+ this.stats.push(stats);
+ if (this.stats.length > 100) this.stats.shift();
+ this.draw();
+ },
+ onStatsLog(statsLog) {
+ statsLog.forEach(stats => this.onStats(stats));
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+root(isDark)
+ &.dual
+ > svg
+ width 50%
+ float left
+
+ &:first-child
+ padding-right 5px
+
+ &:last-child
+ padding-left 5px
+
+ > svg
+ display block
+ padding 10px
+ width 100%
+
+ > text
+ font-size 5px
+ fill isDark ? rgba(#fff, 0.55) : rgba(#000, 0.55)
+
+ > tspan
+ opacity 0.5
+
+ &:after
+ content ""
+ display block
+ clear both
+
+.qpdmibaztplkylerhdbllwcokyrfxeyj[data-darkmode]
+ root(true)
+
+.qpdmibaztplkylerhdbllwcokyrfxeyj:not([data-darkmode])
+ root(false)
+
+</style>
diff --git a/src/client/app/common/views/widgets/server.vue b/src/client/app/common/views/widgets/server.vue
index 2fdd60499b..d796a3ae05 100644
--- a/src/client/app/common/views/widgets/server.vue
+++ b/src/client/app/common/views/widgets/server.vue
@@ -55,11 +55,11 @@ export default define({
this.fetching = false;
});
- this.connection = (this as any).os.streams.serverStream.getConnection();
- this.connectionId = (this as any).os.streams.serverStream.use();
+ this.connection = (this as any).os.streams.serverStatsStream.getConnection();
+ this.connectionId = (this as any).os.streams.serverStatsStream.use();
},
beforeDestroy() {
- (this as any).os.streams.serverStream.dispose(this.connectionId);
+ (this as any).os.streams.serverStatsStream.dispose(this.connectionId);
},
methods: {
toggle() {
diff --git a/src/client/app/desktop/views/components/home.vue b/src/client/app/desktop/views/components/home.vue
index 826753c169..cac1fd935b 100644
--- a/src/client/app/desktop/views/components/home.vue
+++ b/src/client/app/desktop/views/components/home.vue
@@ -23,6 +23,7 @@
<option value="post-form">%i18n:common.widgets.post-form%</option>
<option value="messaging">%i18n:common.widgets.messaging%</option>
<option value="memo">%i18n:common.widgets.memo%</option>
+ <option value="posts-monitor">%i18n:common.widgets.posts-monitor%</option>
<option value="server">%i18n:common.widgets.server%</option>
<option value="donation">%i18n:common.widgets.donation%</option>
<option value="nav">%i18n:common.widgets.nav%</option>
diff --git a/src/client/app/desktop/views/pages/deck/deck.widgets-column.vue b/src/client/app/desktop/views/pages/deck/deck.widgets-column.vue
index 098a580405..2a3a2472dc 100644
--- a/src/client/app/desktop/views/pages/deck/deck.widgets-column.vue
+++ b/src/client/app/desktop/views/pages/deck/deck.widgets-column.vue
@@ -23,6 +23,7 @@
<option value="post-form">%i18n:common.widgets.post-form%</option>
<option value="messaging">%i18n:common.widgets.messaging%</option>
<option value="memo">%i18n:common.widgets.memo%</option>
+ <option value="posts-monitor">%i18n:common.widgets.posts-monitor%</option>
<option value="server">%i18n:common.widgets.server%</option>
<option value="donation">%i18n:common.widgets.donation%</option>
<option value="nav">%i18n:common.widgets.nav%</option>
diff --git a/src/client/app/mios.ts b/src/client/app/mios.ts
index c644e22087..ba3f967a23 100644
--- a/src/client/app/mios.ts
+++ b/src/client/app/mios.ts
@@ -8,7 +8,8 @@ import Progress from './common/scripts/loading';
import Connection from './common/scripts/streaming/stream';
import { HomeStreamManager } from './common/scripts/streaming/home';
import { DriveStreamManager } from './common/scripts/streaming/drive';
-import { ServerStreamManager } from './common/scripts/streaming/server';
+import { ServerStatsStreamManager } from './common/scripts/streaming/server-stats';
+import { NotesStatsStreamManager } from './common/scripts/streaming/notes-stats';
import { MessagingIndexStreamManager } from './common/scripts/streaming/messaging-index';
import { OthelloStreamManager } from './common/scripts/streaming/othello';
@@ -104,14 +105,16 @@ export default class MiOS extends EventEmitter {
localTimelineStream: LocalTimelineStreamManager;
globalTimelineStream: GlobalTimelineStreamManager;
driveStream: DriveStreamManager;
- serverStream: ServerStreamManager;
+ serverStatsStream: ServerStatsStreamManager;
+ notesStatsStream: NotesStatsStreamManager;
messagingIndexStream: MessagingIndexStreamManager;
othelloStream: OthelloStreamManager;
} = {
localTimelineStream: null,
globalTimelineStream: null,
driveStream: null,
- serverStream: null,
+ serverStatsStream: null,
+ notesStatsStream: null,
messagingIndexStream: null,
othelloStream: null
};
@@ -218,7 +221,8 @@ export default class MiOS extends EventEmitter {
this.store = initStore(this);
//#region Init stream managers
- this.streams.serverStream = new ServerStreamManager(this);
+ this.streams.serverStatsStream = new ServerStatsStreamManager(this);
+ this.streams.notesStatsStream = new NotesStatsStreamManager(this);
this.once('signedin', () => {
// Init home stream manager
diff --git a/src/client/app/mobile/views/pages/widgets.vue b/src/client/app/mobile/views/pages/widgets.vue
index eab0ca6a38..ea8580b4d0 100644
--- a/src/client/app/mobile/views/pages/widgets.vue
+++ b/src/client/app/mobile/views/pages/widgets.vue
@@ -15,6 +15,7 @@
<option value="rss">%i18n:common.widgets.rss%</option>
<option value="photo-stream">%i18n:common.widgets.photo-stream%</option>
<option value="slideshow">%i18n:common.widgets.slideshow%</option>
+ <option value="posts-monitor">%i18n:common.widgets.posts-monitor%</option>
<option value="version">%i18n:common.widgets.version%</option>
<option value="server">%i18n:common.widgets.server%</option>
<option value="memo">%i18n:common.widgets.memo%</option>
diff --git a/src/db/mongodb.ts b/src/db/mongodb.ts
index 05bb72bfde..0f6d27ca15 100644
--- a/src/db/mongodb.ts
+++ b/src/db/mongodb.ts
@@ -12,7 +12,10 @@ const uri = u && p
*/
import mongo from 'monk';
-const db = mongo(uri);
+const db = mongo(uri, {
+ poolSize: 16,
+ keepAlive: 1
+});
export default db;
diff --git a/src/index.ts b/src/index.ts
index 42a4f484e6..4a98b7564c 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -18,6 +18,7 @@ import EnvironmentInfo from './utils/environmentInfo';
import MachineInfo from './utils/machineInfo';
import DependencyInfo from './utils/dependencyInfo';
import serverStats from './server-stats';
+import notesStats from './notes-stats';
import loadConfig from './config/load';
import { Config } from './config/types';
@@ -50,6 +51,7 @@ function main() {
ev.mount();
serverStats();
+ notesStats();
} else {
workerMain(opt);
}
diff --git a/src/models/note.ts b/src/models/note.ts
index ad8c1565f0..d4681b7b70 100644
--- a/src/models/note.ts
+++ b/src/models/note.ts
@@ -16,6 +16,9 @@ import Following from './following';
const Note = db.get<INote>('notes');
Note.createIndex('uri', { sparse: true, unique: true });
Note.createIndex('userId');
+Note.createIndex({
+ createdAt: -1
+});
export default Note;
export function isValidText(text: string): boolean {
diff --git a/src/notes-stats-child.ts b/src/notes-stats-child.ts
new file mode 100644
index 0000000000..0e0f14eaf1
--- /dev/null
+++ b/src/notes-stats-child.ts
@@ -0,0 +1,20 @@
+import Note from './models/note';
+
+setInterval(async () => {
+ const [all, local] = await Promise.all([Note.count({
+ createdAt: {
+ $gte: new Date(Date.now() - 3000)
+ }
+ }), Note.count({
+ createdAt: {
+ $gte: new Date(Date.now() - 3000)
+ },
+ '_user.host': null
+ })]);
+
+ const stats = {
+ all, local
+ };
+
+ process.send(stats);
+}, 3000);
diff --git a/src/notes-stats.ts b/src/notes-stats.ts
new file mode 100644
index 0000000000..3094c34af0
--- /dev/null
+++ b/src/notes-stats.ts
@@ -0,0 +1,20 @@
+import * as childProcess from 'child_process';
+import Xev from 'xev';
+
+const ev = new Xev();
+
+export default function() {
+ const log = [];
+
+ const p = childProcess.fork(__dirname + '/notes-stats-child.js');
+
+ p.on('message', stats => {
+ ev.emit('notesStats', stats);
+ log.push(stats);
+ if (log.length > 100) log.shift();
+ });
+
+ ev.on('requestNotesStatsLog', id => {
+ ev.emit('notesStatsLog:' + id, log);
+ });
+}
diff --git a/src/server-stats.ts b/src/server-stats.ts
index 85aa85b682..7b0d4a8576 100644
--- a/src/server-stats.ts
+++ b/src/server-stats.ts
@@ -6,7 +6,7 @@ import Xev from 'xev';
const ev = new Xev();
/**
- * Report stats regularly
+ * Report server stats regularly
*/
export default function() {
const log = [];
diff --git a/src/server/api/stream/notes-stats.ts b/src/server/api/stream/notes-stats.ts
new file mode 100644
index 0000000000..739b325848
--- /dev/null
+++ b/src/server/api/stream/notes-stats.ts
@@ -0,0 +1,35 @@
+import * as websocket from 'websocket';
+import Xev from 'xev';
+
+const ev = new Xev();
+
+export default function(request: websocket.request, connection: websocket.connection): void {
+ const onStats = stats => {
+ connection.send(JSON.stringify({
+ type: 'stats',
+ body: stats
+ }));
+ };
+
+ connection.on('message', async data => {
+ const msg = JSON.parse(data.utf8Data);
+
+ switch (msg.type) {
+ case 'requestLog':
+ ev.once('notesStatsLog:' + msg.id, statsLog => {
+ connection.send(JSON.stringify({
+ type: 'statsLog',
+ body: statsLog
+ }));
+ });
+ ev.emit('requestNotesStatsLog', msg.id);
+ break;
+ }
+ });
+
+ ev.addListener('notesStats', onStats);
+
+ connection.on('close', () => {
+ ev.removeListener('notesStats', onStats);
+ });
+}
diff --git a/src/server/api/stream/server.ts b/src/server/api/stream/server-stats.ts
index 342170a21e..342170a21e 100644
--- a/src/server/api/stream/server.ts
+++ b/src/server/api/stream/server-stats.ts
diff --git a/src/server/api/streaming.ts b/src/server/api/streaming.ts
index 6825b6336a..2d4cfc108f 100644
--- a/src/server/api/streaming.ts
+++ b/src/server/api/streaming.ts
@@ -12,7 +12,8 @@ import messagingStream from './stream/messaging';
import messagingIndexStream from './stream/messaging-index';
import othelloGameStream from './stream/othello-game';
import othelloStream from './stream/othello';
-import serverStream from './stream/server';
+import serverStatsStream from './stream/server-stats';
+import notesStatsStream from './stream/notes-stats';
import requestsStream from './stream/requests';
import { ParsedUrlQuery } from 'querystring';
import authenticate from './authenticate';
@@ -28,8 +29,13 @@ module.exports = (server: http.Server) => {
ws.on('request', async (request) => {
const connection = request.accept();
- if (request.resourceURL.pathname === '/server') {
- serverStream(request, connection);
+ if (request.resourceURL.pathname === '/server-stats') {
+ serverStatsStream(request, connection);
+ return;
+ }
+
+ if (request.resourceURL.pathname === '/notes-stats') {
+ notesStatsStream(request, connection);
return;
}