Commit fcd516b5 authored by Cédric Moreau's avatar Cédric Moreau

[enh] Add --monitor option to show performance traces

parent 8d6139ea
......@@ -42,7 +42,7 @@ module.exports = {
},
cliOptions: [
//{ value: '--option-name <value_type>', desc: 'description for help command'}
{ value: '--monitor', desc: 'Enable performance monitoring of DB access'}
],
cli: [{
......
import {Underscore} from 'duniter/app/lib/common-libs/underscore'
export function getMicrosecondsTime() {
const [ seconds, nanoseconds ] = process.hrtime()
return seconds * 1000000 + nanoseconds / 1000
}
export function getDurationInMicroSeconds(before:number) {
return parseInt(String(getMicrosecondsTime() - before))
}
const monitorings: {
[k: string]: {
times: {
time: number
}[]
}
} = {}
export const showExecutionTimes = () => {
let traces: { name: string, times: number, avg: number, total: number }[] = []
Object
.keys(monitorings)
.forEach(k => {
const m = monitorings[k]
const total = m.times.reduce((s, t) => s + t.time / 1000, 0)
const avg = m.times.length ? total / m.times.length : 0
traces.push({
name: k,
times: m.times.length,
avg,
total
})
})
traces = Underscore.sortBy(traces, t => t.total)
traces
.forEach(t => {
console.log('%s %s times %sms (average) %sms (total time)',
(t.name + ':').padEnd(50, ' '),
String(t.times).padStart(10, ' '),
t.avg.toFixed(3).padStart(10, ' '),
t.total.toFixed(0).padStart(10, ' ')
)
})
}
export const MonitorExecutionTime = function (idProperty?: string) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
if (process.argv.includes('--monitor')) {
const original = descriptor.value
if (original.__proto__.constructor.name === "AsyncFunction") {
descriptor.value = async function (...args: any[]) {
const start = getMicrosecondsTime()
const entities: any[] = await original.apply(this, args)
const duration = getDurationInMicroSeconds(start)
const k = target.constructor.name + '.' + propertyKey + (idProperty ? `[${(this as any)[idProperty]}]` : '')
if (!monitorings[k]) {
monitorings[k] = {
times: []
}
}
monitorings[k].times.push({
time: duration
})
return entities
}
} else {
descriptor.value = function (...args: any[]) {
const start = getMicrosecondsTime()
const entities: any[] = original.apply(this, args)
const duration = getDurationInMicroSeconds(start)
const k = target.constructor.name + '.' + propertyKey + (idProperty ? `[${(this as any)[idProperty]}]` : '')
if (!monitorings[k]) {
monitorings[k] = {
times: []
}
}
monitorings[k].times.push({
time: duration
})
return entities
}
}
}
}
}
const _underscore_ = require("underscore")
export interface Map<T> {
[k:string]: T
}
export interface UnderscoreClass<T> {
filter(filterFunc: (t: T) => boolean): UnderscoreClass<T>
where(props: { [k in keyof T]?: T[k] }): UnderscoreClass<T>
sortBy(sortFunc:(element:T) => number): UnderscoreClass<T>
pluck<K extends keyof T>(k:K): UnderscoreClass<T>
uniq<K extends keyof T>(isSorted?:boolean, iteratee?:(t:T) => K): UnderscoreClass<T>
value(): T[]
}
export const Underscore = {
filter: <T>(elements:T[], filterFunc: (t:T) => boolean): T[] => {
return _underscore_.filter(elements, filterFunc)
},
where: <T>(elements:T[], props: { [k in keyof T]?: T[k] }): T[] => {
return _underscore_.where(elements, props)
},
findWhere: <T>(elements:T[], props: { [k in keyof T]?: T[k] }): T|null => {
return _underscore_.findWhere(elements, props)
},
keys: <T>(map:T): (keyof T)[] => {
return _underscore_.keys(map)
},
values: <T>(map:{ [k:string]: T }): T[] => {
return _underscore_.values(map)
},
pluck: <T, K extends keyof T>(elements:T[], k:K): T[K][] => {
return _underscore_.pluck(elements, k)
},
pick: <T, K extends keyof T>(elements:T, ...k:K[]): T[K][] => {
return _underscore_.pick(elements, ...k)
},
omit: <T, K extends keyof T>(element:T, ...k:K[]): T[K][] => {
return _underscore_.omit(element, ...k)
},
uniq: <T, K>(elements:T[], isSorted:boolean = false, iteratee?:(t:T) => K): T[] => {
return _underscore_.uniq(elements, isSorted, iteratee)
},
clone: <T>(t:T): T => {
return _underscore_.clone(t)
},
mapObject: <T, K extends keyof T, L extends keyof (T[K])>(t:T, cb:(k:K) => (T[K])[L]): Map<T[K][L]> => {
return _underscore_.mapObject(t, cb)
},
mapObjectByProp: <T, K extends keyof T, L extends keyof (T[K])>(t:T, prop:L): Map<T[K][L]> => {
return _underscore_.mapObject(t, (o:T[K]) => o[prop])
},
sortBy: <T, K extends keyof T>(elements:T[], sortFunc:((element:T) => number|string)|K): T[] => {
return _underscore_.sortBy(elements, sortFunc)
},
difference: <T>(array1:T[], array2:T[]): T[] => {
return _underscore_.difference(array1, array2)
},
shuffle: <T>(elements:T[]): T[] => {
return _underscore_.shuffle(elements)
},
extend: <T, U>(t1:T, t2:U): T|U => {
return _underscore_.extend(t1, t2)
},
range: (count:number, end?:number): number[] => {
return _underscore_.range(count, end)
},
chain: <T>(element:T[]): UnderscoreClass<T> => {
return _underscore_.chain(element)
},
}
import {Server} from 'duniter/server'
import {DBMembership} from 'duniter/app/lib/dal/sqliteDAL/MembershipDAL'
import {DBIdentity} from 'duniter/app/lib/dal/sqliteDAL/IdentityDAL'
import {MonitorExecutionTime, showExecutionTimes} from '../lib/MonitorExecutionTime'
const constants = require(__dirname + '/../lib/constants')
const timestampToDatetime = require(__dirname + '/../lib/timestampToDatetime')
......@@ -461,6 +462,8 @@ module.exports = async (req: any, res: any, next: any) => {
lockWillMembers = false;
}
showExecutionTimes()
// Si le client demande la réponse au format JSON, le faire
if (format == 'JSON')
{
......@@ -570,37 +573,45 @@ class DataFinder {
constructor(protected duniterServer: Server) {
}
@MonitorExecutionTime()
findPendingMembers() {
return this.query('SELECT `buid`,`pubkey`,`uid`,`hash`,`expires_on`,`revocation_sig` FROM identities_pending WHERE `member`=0')
}
@MonitorExecutionTime()
findPendingCertsToTarget(toPubkey: string, hash: string) {
return DataFinder.getFromCacheOrDB(this.memCertsToTarget, [toPubkey, hash].join('-'), () => this.query(
'SELECT `from`,`block_number`,`block_hash`,`expires_on` FROM certifications_pending WHERE `to`=\''+toPubkey+'\' AND `target`=\''+hash+'\' ORDER BY `expires_on` DESC'))
}
@MonitorExecutionTime()
getWotexInfos(uid: string) {
return this.duniterServer.dal.idtyDAL.query('' +
'SELECT hash, uid, pub, wotb_id FROM i_index WHERE uid = ? ' +
'UNION ALL ' + 'SELECT hash, uid, pubkey as pub, (SELECT NULL) AS wotb_id FROM idty WHERE uid = ?', [uid, uid])
}
@MonitorExecutionTime()
async getBlockMedianTimeAndHash(block_number: number): Promise<{ hash: string, medianTime: number }|undefined> {
return (await DataFinder.getFromCacheOrDB(this.memBlocks, String(block_number),() => this.duniterServer.dal.getBlock(block_number))) || undefined
}
@MonitorExecutionTime()
getUidOfPub(pub: string): Promise<{ uid: string }[]> {
return DataFinder.getFromCacheOrDB(this.memUidFromPub, pub, () => this.query('SELECT `uid` FROM i_index WHERE `pub`=\''+pub+'\' LIMIT 1'))
}
@MonitorExecutionTime()
async getWotbIdByIssuerPubkey(issuerPubkey: string) {
return DataFinder.getFromCacheOrDB(this.memWotbIdFromPub, issuerPubkey, async () => (await this.duniterServer.dal.iindexDAL.query('SELECT wotb_id FROM i_index WHERE pub = ? AND wotb_id IS NOT NULL', [issuerPubkey]))[0].wotb_id)
}
@MonitorExecutionTime()
getChainableOnByIssuerPubkey(issuerPubkey: string) {
return this.query('SELECT `chainable_on` FROM c_index WHERE `issuer`=\''+issuerPubkey+'\' ORDER BY `chainable_on` DESC LIMIT 1')
}
@MonitorExecutionTime()
getCurrentBlockOrNull() {
return this.duniterServer.dal.getCurrentBlockOrNull()
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment