1 | <!DOCTYPE html>
|
2 | <html lang="en">
|
3 | <head>
|
4 | <meta charset="UTF-8">
|
5 | <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 | <meta http-equiv="X-UA-Compatible" content="ie=edge">
|
7 | <title>Radi.js - DBMonster</title>
|
8 | <link rel="stylesheet" href="assets/bootstrap.min.css">
|
9 | <style>
|
10 |
|
11 | table {
|
12 | font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
|
13 | font-size: 14px;
|
14 | line-height: 1.42857143;
|
15 | color: #333;
|
16 | background-color: #fff;
|
17 | }
|
18 |
|
19 | #link {
|
20 | position: fixed;
|
21 | top: 0; right: 0;
|
22 | font-size: 12px;
|
23 | padding: 5px 10px;
|
24 | background: rgba(255,255,255,0.85);
|
25 | z-index: 5;
|
26 | box-shadow: 0 0 8px rgba(0,0,0,0.6);
|
27 | }
|
28 | #link .center {
|
29 | display: block;
|
30 | text-align: center;
|
31 | }
|
32 |
|
33 | .Query {
|
34 | position: relative;
|
35 | }
|
36 |
|
37 | .Query:hover .popover {
|
38 | left: -100%;
|
39 | width: 100%;
|
40 | display: block;
|
41 | }
|
42 | </style>
|
43 | </head>
|
44 | <body>
|
45 | <div id="app"></div>
|
46 | <script>
|
47 |
|
48 | var Monitoring = Monitoring || (function() {
|
49 |
|
50 | var RenderRate = function () {
|
51 | var container = document.createElement( 'div' );
|
52 | container.id = 'stats';
|
53 | container.style.cssText = 'width:150px;opacity:0.9;cursor:pointer;position:fixed;right:80px;bottom:0px;';
|
54 |
|
55 | var msDiv = document.createElement( 'div' );
|
56 | msDiv.id = 'ms';
|
57 | msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;';
|
58 | container.appendChild( msDiv );
|
59 |
|
60 | var msText = document.createElement( 'div' );
|
61 | msText.id = 'msText';
|
62 | msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px';
|
63 | msText.innerHTML= 'Repaint rate: 0/sec';
|
64 | msDiv.appendChild( msText );
|
65 |
|
66 | var self = this;
|
67 | var rate = 0;
|
68 | var bucketSize = 20;
|
69 | var bucket = [];
|
70 | var lastTime = Date.now();
|
71 | return {
|
72 | domElement: container,
|
73 | ping: function () {
|
74 | var start = lastTime;
|
75 | var stop = Date.now();
|
76 | var rate = 1000 / (stop - start);
|
77 | if (rate == Infinity) {
|
78 | return;
|
79 | }
|
80 | bucket.push(rate);
|
81 | if (bucket.length > bucketSize) {
|
82 | bucket.shift();
|
83 | }
|
84 | var sum = 0;
|
85 | for (var i = 0; i < bucket.length; i++) {
|
86 | sum = sum + bucket[i];
|
87 | }
|
88 | self.rate = (sum / bucket.length);
|
89 | msText.textContent = "Repaint rate: " + self.rate.toFixed(2) + "/sec";
|
90 | lastTime = stop;
|
91 | },
|
92 | rate: function () {
|
93 | return self.rate;
|
94 | }
|
95 | }
|
96 | };
|
97 |
|
98 | var renderRate = new RenderRate();
|
99 | document.body.appendChild( renderRate.domElement );
|
100 |
|
101 | return {
|
102 | memoryStats: stats,
|
103 | renderRate: renderRate
|
104 | };
|
105 |
|
106 | })();
|
107 |
|
108 | var ENV = ENV || (function() {
|
109 |
|
110 | var first = true;
|
111 | var counter = 0;
|
112 | var data;
|
113 | var _base;
|
114 | (_base = String.prototype).lpad || (_base.lpad = function(padding, toLength) {
|
115 | return padding.repeat((toLength - this.length) / padding.length).concat(this);
|
116 | });
|
117 |
|
118 | function formatElapsed(value) {
|
119 | var str = parseFloat(value).toFixed(2);
|
120 | if (value > 60) {
|
121 | minutes = Math.floor(value / 60);
|
122 | comps = (value % 60).toFixed(2).split('.');
|
123 | seconds = comps[0].lpad('0', 2);
|
124 | ms = comps[1];
|
125 | str = minutes + ":" + seconds + "." + ms;
|
126 | }
|
127 | return str;
|
128 | }
|
129 |
|
130 | function getElapsedClassName(elapsed) {
|
131 | var className = 'Query elapsed';
|
132 | if (elapsed >= 10.0) {
|
133 | className += ' warn_long';
|
134 | }
|
135 | else if (elapsed >= 1.0) {
|
136 | className += ' warn';
|
137 | }
|
138 | else {
|
139 | className += ' short';
|
140 | }
|
141 | return className;
|
142 | }
|
143 |
|
144 | function countClassName(queries) {
|
145 | var countClassName = "label";
|
146 | if (queries >= 20) {
|
147 | countClassName += " label-important";
|
148 | }
|
149 | else if (queries >= 10) {
|
150 | countClassName += " label-warning";
|
151 | }
|
152 | else {
|
153 | countClassName += " label-success";
|
154 | }
|
155 | return countClassName;
|
156 | }
|
157 |
|
158 | function updateQuery(object) {
|
159 | if (!object) {
|
160 | object = {};
|
161 | }
|
162 | var elapsed = Math.random() * 15;
|
163 | object.elapsed = elapsed;
|
164 | object.formatElapsed = formatElapsed(elapsed);
|
165 | object.elapsedClassName = getElapsedClassName(elapsed);
|
166 | object.query = "SELECT blah FROM something";
|
167 | object.waiting = Math.random() < 0.5;
|
168 | if (Math.random() < 0.2) {
|
169 | object.query = "<IDLE> in transaction";
|
170 | }
|
171 | if (Math.random() < 0.1) {
|
172 | object.query = "vacuum";
|
173 | }
|
174 | return object;
|
175 | }
|
176 |
|
177 | function cleanQuery(value) {
|
178 | if (value) {
|
179 | value.formatElapsed = "";
|
180 | value.elapsedClassName = "";
|
181 | value.query = "";
|
182 | value.elapsed = null;
|
183 | value.waiting = null;
|
184 | } else {
|
185 | return {
|
186 | query: "***",
|
187 | formatElapsed: "",
|
188 | elapsedClassName: ""
|
189 | };
|
190 | }
|
191 | }
|
192 |
|
193 | function generateRow(object, keepIdentity, counter) {
|
194 | var nbQueries = Math.floor((Math.random() * 10) + 1);
|
195 | if (!object) {
|
196 | object = {};
|
197 | }
|
198 | object.lastMutationId = counter;
|
199 | object.nbQueries = nbQueries;
|
200 | if (!object.lastSample) {
|
201 | object.lastSample = {};
|
202 | }
|
203 | if (!object.lastSample.topFiveQueries) {
|
204 | object.lastSample.topFiveQueries = [];
|
205 | }
|
206 | if (keepIdentity) {
|
207 |
|
208 | if (!object.lastSample.queries) {
|
209 | object.lastSample.queries = [];
|
210 | for (var l = 0; l < 12; l++) {
|
211 | object.lastSample.queries[l] = cleanQuery();
|
212 | }
|
213 | }
|
214 | for (var j in object.lastSample.queries) {
|
215 | var value = object.lastSample.queries[j];
|
216 | if (j <= nbQueries) {
|
217 | updateQuery(value);
|
218 | } else {
|
219 | cleanQuery(value);
|
220 | }
|
221 | }
|
222 | } else {
|
223 | object.lastSample.queries = [];
|
224 | for (var j = 0; j < 12; j++) {
|
225 | if (j < nbQueries) {
|
226 | var value = updateQuery(cleanQuery());
|
227 | object.lastSample.queries.push(value);
|
228 | } else {
|
229 | object.lastSample.queries.push(cleanQuery());
|
230 | }
|
231 | }
|
232 | }
|
233 | for (var i = 0; i < 5; i++) {
|
234 | var source = object.lastSample.queries[i];
|
235 | object.lastSample.topFiveQueries[i] = source;
|
236 | }
|
237 | object.lastSample.nbQueries = nbQueries;
|
238 | object.lastSample.countClassName = countClassName(nbQueries);
|
239 | return object;
|
240 | }
|
241 |
|
242 | function getData(keepIdentity) {
|
243 | var oldData = data;
|
244 | if (!keepIdentity) {
|
245 | data = [];
|
246 | for (var i = 1; i <= ENV.rows; i++) {
|
247 | data.push({ dbname: 'cluster' + i, query: "", formatElapsed: "", elapsedClassName: "" });
|
248 | data.push({ dbname: 'cluster' + i + ' slave', query: "", formatElapsed: "", elapsedClassName: "" });
|
249 | }
|
250 | }
|
251 | if (!data) {
|
252 | data = [];
|
253 | for (var i = 1; i <= ENV.rows; i++) {
|
254 | data.push({ dbname: 'cluster' + i });
|
255 | data.push({ dbname: 'cluster' + i + ' slave' });
|
256 | }
|
257 | oldData = data;
|
258 | }
|
259 | for (var i in data) {
|
260 | var row = data[i];
|
261 | if (!keepIdentity && oldData && oldData[i]) {
|
262 | row.lastSample = oldData[i].lastSample;
|
263 | }
|
264 | if (!row.lastSample || Math.random() < ENV.mutations()) {
|
265 | counter = counter + 1;
|
266 | if (!keepIdentity) {
|
267 | row.lastSample = null;
|
268 | }
|
269 | generateRow(row, keepIdentity, counter);
|
270 | } else {
|
271 | data[i] = oldData[i];
|
272 | }
|
273 | }
|
274 | first = false;
|
275 | return {
|
276 | toArray: function() {
|
277 | return data;
|
278 | }
|
279 | };
|
280 | }
|
281 |
|
282 | var mutationsValue = 0.5;
|
283 |
|
284 | function mutations(value) {
|
285 | if (value) {
|
286 | mutationsValue = value;
|
287 | document.querySelector('#ratioval').innerHTML = 'mutations : ' + (mutationsValue * 100).toFixed(0) + '%';
|
288 | return mutationsValue;
|
289 | } else {
|
290 | return mutationsValue;
|
291 | }
|
292 | }
|
293 |
|
294 | var body = document.querySelector('body');
|
295 | var theFirstChild = body.firstChild;
|
296 |
|
297 | var sliderContainer = document.createElement('div');
|
298 | sliderContainer.style.cssText = "display: flex";
|
299 | var slider = document.createElement('input');
|
300 | var text = document.createElement('label');
|
301 | text.innerHTML = 'mutations : ' + (mutationsValue * 100).toFixed(0) + '%';
|
302 | text.id = "ratioval";
|
303 | slider.setAttribute("type", "range");
|
304 | slider.style.cssText = 'margin-bottom: 10px; margin-top: 5px';
|
305 | slider.addEventListener('change', function(e) {
|
306 | ENV.mutations(e.target.value / 100);
|
307 | });
|
308 | sliderContainer.appendChild(text);
|
309 | sliderContainer.appendChild(slider);
|
310 | body.insertBefore(sliderContainer, theFirstChild);
|
311 |
|
312 | return {
|
313 | generateData: getData,
|
314 | rows: 50,
|
315 | timeout: 0,
|
316 | mutations: mutations
|
317 | };
|
318 | })();
|
319 | </script>
|
320 | <script src="http://mathieuancelin.github.io/js-repaint-perfs/lib/memory-stats.js"></script>
|
321 | <script src="http://mathieuancelin.github.io/js-repaint-perfs/lib/monitor.js"></script>
|
322 | <script src="../../dist/radi.js"></script>
|
323 |
|
324 | <script>
|
325 | const { r, l, component, mount } = radi;
|
326 |
|
327 | var raf = requestAnimationFrame
|
328 | || msRequestAnimationFrame
|
329 | || mozRequestAnimationFrame
|
330 | || webkitRequestAnimationFrame
|
331 | || oRequestAnimationFrame
|
332 | || setTimeout;
|
333 |
|
334 | const main = component({
|
335 | view: function () {
|
336 | return r('table',
|
337 | { class: 'table table-striped latest-data' },
|
338 | r('tbody',
|
339 | list(l(this.dbs), (db, i) => {
|
340 | return r('tr',
|
341 | r('td',
|
342 | { class: 'dbname' },
|
343 | db.dbname
|
344 | ),
|
345 | r('td',
|
346 | { class: 'query-count' },
|
347 | r('span',
|
348 | {
|
349 | class: l(db.lastSample.countClassName)
|
350 | },
|
351 | l(db.nbQueries)
|
352 | )
|
353 | ),
|
354 | list(l(db.lastSample.topFiveQueries), (q, i) => {
|
355 | return r('td',
|
356 | {
|
357 | class: l('Query ' + q.elapsedClassName)
|
358 | },
|
359 | l(q.formatElapsed),
|
360 | r('div',
|
361 | { class: 'popover left' },
|
362 | r('div',
|
363 | { class: 'popover-content' },
|
364 | l(q.query)
|
365 | ),
|
366 | r('div', { class: 'arrow' })
|
367 | )
|
368 | )
|
369 | })
|
370 | );
|
371 | })
|
372 | )
|
373 | );
|
374 | },
|
375 | state: {
|
376 | dbs: []
|
377 | },
|
378 | actions: {
|
379 | onMount() {
|
380 | this.dbs = [];
|
381 | this.load()
|
382 | },
|
383 | load() {
|
384 | this.dbs = ENV.generateData(true).toArray();
|
385 | Monitoring.renderRate.ping();
|
386 | setTimeout(this.load, ENV.timeout);
|
387 | }
|
388 | }
|
389 | });
|
390 |
|
391 | mount(new main(), 'app');
|
392 | </script>
|
393 | </body>
|
394 | </html>
|