Skip to content

Commit

Permalink
UBERF-4319: Trigger Server queries
Browse files Browse the repository at this point in the history
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
  • Loading branch information
haiodo committed Feb 6, 2024
1 parent 68d7a5b commit 2a17441
Show file tree
Hide file tree
Showing 12 changed files with 358 additions and 180 deletions.
1 change: 1 addition & 0 deletions models/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export class TDoc extends TObj implements Doc {

@Prop(TypeTimestamp(), core.string.CreatedDate)
@ReadOnly()
@Index(IndexKind.IndexedDsc)
createdOn!: Timestamp
}

Expand Down
22 changes: 12 additions & 10 deletions packages/core/src/measurements/context.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
// Basic performance metrics suite.

import { childMetrics, measure, newMetrics } from './metrics'
import { MeasureContext, MeasureLogger, Metrics, ParamType } from './types'
import { FullParamsType, MeasureContext, MeasureLogger, Metrics, ParamsType } from './types'

/**
* @public
*/
export class MeasureMetricsContext implements MeasureContext {
private readonly name: string
private readonly params: Record<string, ParamType>
private readonly params: ParamsType
logger: MeasureLogger
metrics: Metrics
private readonly done: (value?: number) => void

constructor (
name: string,
params: Record<string, ParamType>,
params: ParamsType,
fullParams: FullParamsType = {},
metrics: Metrics = newMetrics(),
logger?: MeasureLogger
) {
this.name = name
this.params = params
this.metrics = metrics
this.done = measure(metrics, params)
this.done = measure(metrics, params, fullParams)

this.logger = logger ?? {
info: (msg, args) => {
Expand All @@ -35,20 +36,21 @@ export class MeasureMetricsContext implements MeasureContext {
}

measure (name: string, value: number): void {
const c = new MeasureMetricsContext('#' + name, {}, childMetrics(this.metrics, ['#' + name]))
const c = new MeasureMetricsContext('#' + name, {}, {}, childMetrics(this.metrics, ['#' + name]))
c.done(value)
}

newChild (name: string, params: Record<string, ParamType>, logger?: MeasureLogger): MeasureContext {
return new MeasureMetricsContext(name, params, childMetrics(this.metrics, [name]), logger)
newChild (name: string, params: ParamsType, fullParams?: FullParamsType, logger?: MeasureLogger): MeasureContext {
return new MeasureMetricsContext(name, params, fullParams ?? {}, childMetrics(this.metrics, [name]), logger)
}

async with<T>(
name: string,
params: Record<string, ParamType>,
op: (ctx: MeasureContext) => T | Promise<T>
params: ParamsType,
op: (ctx: MeasureContext) => T | Promise<T>,
fullParams?: ParamsType
): Promise<T> {
const c = this.newChild(name, params)
const c = this.newChild(name, params, fullParams)
try {
let value = op(c)
if (value instanceof Promise) {
Expand Down
32 changes: 29 additions & 3 deletions packages/core/src/measurements/metrics.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Basic performance metrics suite.

import { MetricsData } from '.'
import { Metrics, ParamType } from './types'
import { cutObjectArray } from '../utils'
import { FullParamsType, Metrics, ParamsType } from './types'

/**
* @public
Expand All @@ -21,11 +22,30 @@ export function newMetrics (): Metrics {
}
}

function getUpdatedTopResult (
current: Metrics['topResult'],
time: number,
params: FullParamsType
): Metrics['topResult'] {
if (current === undefined || current.length < 3 || current.some((it) => it.value < time)) {
const result = [
...(current ?? []),
{
value: time,
params: cutObjectArray(params)
}
]
result.sort((a, b) => b.value - a.value)
return result.slice(0, 3)
}
return current
}

/**
* Measure with tree expansion. Operation counter will be added only to leaf's.
* @public
*/
export function measure (metrics: Metrics, params: Record<string, ParamType>): () => void {
export function measure (metrics: Metrics, params: ParamsType, fullParams: FullParamsType = {}): () => void {
const st = Date.now()
return (value?: number) => {
const ed = Date.now()
Expand All @@ -47,10 +67,14 @@ export function measure (metrics: Metrics, params: Record<string, ParamType>): (
}
param.value += value ?? ed - st
param.operations++

param.topResult = getUpdatedTopResult(param.topResult, ed - st, fullParams)
}
// Update leaf data
metrics.value += value ?? ed - st
metrics.operations++

metrics.topResult = getUpdatedTopResult(metrics.topResult, ed - st, fullParams)
}
}

Expand Down Expand Up @@ -84,11 +108,13 @@ export function metricsAggregate (m: Metrics): Metrics {
.reduce((p, v) => {
return p + v.value
}, 0)

return {
operations: m.operations,
measurements: ms,
params: m.params,
value: sumVal
value: sumVal,
topResult: m.topResult
}
}

Expand Down
23 changes: 21 additions & 2 deletions packages/core/src/measurements/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,26 @@
*/
export type ParamType = string | number | boolean | undefined

/**
* @public
*/
export type ParamsType = Record<string, ParamType>

/**
* @public
*/
export type FullParamsType = Record<string, any>

/**
* @public
*/
export interface MetricsData {
operations: number
value: number
topResult?: {
value: number
params: FullParamsType
}[]
}

/**
Expand All @@ -31,9 +45,14 @@ export interface MeasureLogger {
*/
export interface MeasureContext {
// Create a child metrics context
newChild: (name: string, params: Record<string, ParamType>, logger?: MeasureLogger) => MeasureContext
newChild: (name: string, params: ParamsType, fullParams?: FullParamsType, logger?: MeasureLogger) => MeasureContext

with: <T>(name: string, params: Record<string, ParamType>, op: (ctx: MeasureContext) => T | Promise<T>) => Promise<T>
with: <T>(
name: string,
params: ParamsType,
op: (ctx: MeasureContext) => T | Promise<T>,
fullParams?: FullParamsType
) => Promise<T>

logger: MeasureLogger

Expand Down
1 change: 1 addition & 0 deletions packages/core/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export interface ServerStorage extends LowLevelStorage {
query: DocumentQuery<T>,
options?: FindOptions<T> & {
domain?: Domain // Allow to find for Doc's in specified domain only.
prefix?: string
}
) => Promise<FindResult<T>>
searchFulltext: (ctx: MeasureContext, query: SearchQuery, options: SearchOptions) => Promise<SearchResult>
Expand Down
50 changes: 38 additions & 12 deletions packages/query/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,25 @@

import core, {
AttachedDoc,
checkMixinKey,
BulkUpdateEvent,
Class,
Client,
DOMAIN_MODEL,
Doc,
DocumentQuery,
DOMAIN_MODEL,
FindOptions,
findProperty,
FindResult,
generateId,
getObjectValue,
Hierarchy,
IndexingUpdateEvent,
BulkUpdateEvent,
Lookup,
LookupData,
matchQuery,
ModelDb,
Ref,
resultSort,
ReverseLookups,
SearchOptions,
SearchQuery,
SearchResult,
SortingQuery,
toFindResult,
Tx,
TxCollectionCUD,
TxCreateDoc,
Expand All @@ -49,9 +45,13 @@ import core, {
TxWorkspaceEvent,
WithLookup,
WorkspaceEvent,
SearchQuery,
SearchOptions,
SearchResult
checkMixinKey,
findProperty,
generateId,
getObjectValue,
matchQuery,
resultSort,
toFindResult
} from '@hcengineering/core'
import { deepEqual } from 'fast-equals'

Expand Down Expand Up @@ -340,6 +340,32 @@ export class LiveQuery extends TxProcessor implements Client {
}
}

async queryFind<T extends Doc>(
_class: Ref<Class<T>>,
query: DocumentQuery<T>,
options?: FindOptions<T>
): Promise<FindResult<T>> {
const current = this.findQuery(_class, query, options)
if (current === undefined) {
const q = this.createQuery(
_class,
query,
{
callback: () => {
// do nothing
},
callbackId: generateId()
},
options
)
if (q.result instanceof Promise) {
q.result = await q.result
}
return toFindResult(this.clone(q.result), q.total) as FindResult<T>
}
return toFindResult(this.clone((current?.result as T[]) ?? []), current.total)
}

private async checkSearch (q: Query, _id: Ref<Doc>): Promise<boolean> {
const match = await this.client.findOne(q._class, { $search: q.query.$search, _id }, q.options)
if (q.result instanceof Promise) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,29 @@
</div>
</FixedColumn>
</svelte:fragment>
{#if metrics.topResult !== undefined && metrics.topResult.length > 0 && metrics.topResult[0].value > 0 && Object.keys(metrics.topResult[0].params).length > 0}
<div class="p-1" style:margin-left={`${level * 0.5 + 0.5}rem`}>
<Expandable>
<svelte:fragment slot="title">
<div class="flex-row-center flex-between flex-grow ml-2">
Slowest result:{metrics.topResult[0].value}
</div>
</svelte:fragment>
{#each metrics.topResult ?? [] as r}
<Expandable>
<svelte:fragment slot="title">
<div class="flex-row-center flex-between flex-grow">
Time:{r.value}
</div>
</svelte:fragment>
<pre>
{JSON.stringify(r, null, 2)}
</pre>
</Expandable>
{/each}
</Expandable>
</div>
{/if}
{#each Object.entries(metrics.measurements) as [k, v], i (k)}
<div style:margin-left={`${level * 0.5}rem`}>
<svelte:self metrics={v} name="{i}. {k}" level={level + 1} />
Expand All @@ -61,7 +84,12 @@
{#each Object.entries(metrics.params) as [k, v], i}
<div style:margin-left={`${level * 0.5}rem`}>
{#each Object.entries(v).toSorted((a, b) => b[1].value / (b[1].operations + 1) - a[1].value / (a[1].operations + 1)) as [kk, vv]}
<Expandable expandable={false} bordered showChevron={false} contentColor>
{@const childExpandable =
vv.topResult !== undefined &&
vv.topResult.length > 0 &&
vv.topResult[0].value > 0 &&
Object.keys(vv.topResult[0].params).length > 0}
<Expandable expandable={childExpandable} bordered showChevron={childExpandable} contentColor>
<svelte:fragment slot="title">
<div class="flex-row-center flex-between flex-grow">
# {k} = {kk}
Expand All @@ -71,11 +99,29 @@
<FixedColumn key="row">
<div class="flex-row-center flex-between">
<FixedColumn key="ops">{vv.operations}</FixedColumn>
<FixedColumn key="time">{showAvg(kk, vv.value, vv.operations)}</FixedColumn>
<FixedColumn key="time">
{showAvg(kk, vv.value, vv.operations)}
</FixedColumn>
<FixedColumn key="time-full">{vv.value}</FixedColumn>
</div>
</FixedColumn>
</svelte:fragment>
{#if childExpandable}
<div class="p-1" style:margin-left={`${level * 0.5 + 0.5}rem`}>
{#each vv.topResult ?? [] as r}
<Expandable>
<svelte:fragment slot="title">
<div class="flex-row-center flex-between flex-grow">
Time:{r.value}
</div>
</svelte:fragment>
<pre>
{JSON.stringify(r, null, 2)}
</pre>
</Expandable>
{/each}
</div>
{/if}
</Expandable>
{/each}
</div>
Expand Down
1 change: 1 addition & 0 deletions server/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"@hcengineering/core": "^0.6.28",
"@hcengineering/platform": "^0.6.9",
"@hcengineering/minio": "^0.6.0",
"@hcengineering/query": "^0.6.8",
"fast-equals": "^2.0.3",
"html-to-text": "^9.0.3"
}
Expand Down
Loading

0 comments on commit 2a17441

Please sign in to comment.