UBERF-4319: Performance changes (#4474)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev
2024-01-30 18:07:34 +07:00
committed by GitHub
parent d02e88737d
commit e6a35d2a03
37 changed files with 775 additions and 436 deletions
+4 -1
View File
@@ -119,7 +119,10 @@ describe('client', () => {
upload: async (domain: Domain, docs: Doc[]) => {},
clean: async (domain: Domain, docs: Ref<Doc>[]) => {},
loadModel: async (last: Timestamp) => txes,
getAccount: async () => null as unknown as Account
getAccount: async () => null as unknown as Account,
measure: async () => {
return async () => ({ time: 0, serverTime: 0 })
}
}
}
const spyCreate = jest.spyOn(TxProcessor, 'createDoc2Doc')
+2 -1
View File
@@ -71,6 +71,7 @@ export async function connect (handler: (tx: Tx) => void): Promise<ClientConnect
upload: async (domain: Domain, docs: Doc[]) => {},
clean: async (domain: Domain, docs: Ref<Doc>[]) => {},
loadModel: async (last: Timestamp) => txes,
getAccount: async () => null as unknown as Account
getAccount: async () => null as unknown as Account,
measure: async () => async () => ({ time: 0, serverTime: 0 })
}
}
+19 -6
View File
@@ -19,8 +19,8 @@ import { Account, AttachedDoc, Class, DOMAIN_MODEL, Doc, Domain, PluginConfigura
import core from './component'
import { Hierarchy } from './hierarchy'
import { ModelDb } from './memdb'
import type { DocumentQuery, FindOptions, FindResult, Storage, FulltextStorage, TxResult, WithLookup } from './storage'
import { SortingOrder, SearchQuery, SearchOptions, SearchResult } from './storage'
import type { DocumentQuery, FindOptions, FindResult, FulltextStorage, Storage, TxResult, WithLookup } from './storage'
import { SearchOptions, SearchQuery, SearchResult, SortingOrder } from './storage'
import { Tx, TxCUD, TxCollectionCUD, TxCreateDoc, TxProcessor, TxUpdateDoc } from './tx'
import { toFindResult } from './utils'
@@ -46,10 +46,17 @@ export interface Client extends Storage, FulltextStorage {
close: () => Promise<void>
}
export type MeasureDoneOperation = () => Promise<{ time: number, serverTime: number }>
export interface MeasureClient extends Client {
// Will perform on server operation measure and will return a local client time and on server time
measure: (operationName: string) => Promise<MeasureDoneOperation>
}
/**
* @public
*/
export interface AccountClient extends Client {
export interface AccountClient extends MeasureClient {
getAccount: () => Promise<Account>
}
@@ -86,9 +93,11 @@ export interface ClientConnection extends Storage, FulltextStorage, BackupClient
// If hash is passed, will return LoadModelResponse
loadModel: (last: Timestamp, hash?: string) => Promise<Tx[] | LoadModelResponse>
getAccount: () => Promise<Account>
measure: (operationName: string) => Promise<MeasureDoneOperation>
}
class ClientImpl implements AccountClient, BackupClient {
class ClientImpl implements AccountClient, BackupClient, MeasureClient {
notify?: (tx: Tx) => void
hierarchy!: Hierarchy
model!: ModelDb
@@ -151,6 +160,10 @@ class ClientImpl implements AccountClient, BackupClient {
return result
}
async measure (operationName: string): Promise<MeasureDoneOperation> {
return await this.conn.measure(operationName)
}
async updateFromRemote (tx: Tx): Promise<void> {
if (tx.objectSpace === core.space.Model) {
this.hierarchy.tx(tx)
@@ -402,14 +415,14 @@ async function buildModel (
try {
hierarchy.tx(tx)
} catch (err: any) {
console.error('failed to apply model transaction, skipping', JSON.stringify(tx), err)
console.error('failed to apply model transaction, skipping', tx._id, tx._class, err?.message)
}
}
for (const tx of txes) {
try {
await model.tx(tx)
} catch (err: any) {
console.error('failed to apply model transaction, skipping', JSON.stringify(tx), err)
console.error('failed to apply model transaction, skipping', tx._id, tx._class, err?.message)
}
}
}
+16 -7
View File
@@ -13,13 +13,18 @@ export class MeasureMetricsContext implements MeasureContext {
metrics: Metrics
private readonly done: (value?: number) => void
constructor (name: string, params: Record<string, ParamType>, metrics: Metrics = newMetrics()) {
constructor (
name: string,
params: Record<string, ParamType>,
metrics: Metrics = newMetrics(),
logger?: MeasureLogger
) {
this.name = name
this.params = params
this.metrics = metrics
this.done = measure(metrics, params)
this.logger = {
this.logger = logger ?? {
info: (msg, args) => {
console.info(msg, ...args)
},
@@ -34,8 +39,8 @@ export class MeasureMetricsContext implements MeasureContext {
c.done(value)
}
newChild (name: string, params: Record<string, ParamType>): MeasureContext {
return new MeasureMetricsContext(name, params, childMetrics(this.metrics, [name]))
newChild (name: string, params: Record<string, ParamType>, logger?: MeasureLogger): MeasureContext {
return new MeasureMetricsContext(name, params, childMetrics(this.metrics, [name]), logger)
}
async with<T>(
@@ -52,13 +57,17 @@ export class MeasureMetricsContext implements MeasureContext {
c.end()
return value
} catch (err: any) {
await c.error(err)
await c.error('Error during:' + name, err)
throw err
}
}
async error (err: Error | string): Promise<void> {
console.error(err)
async error (message: string, ...args: any[]): Promise<void> {
this.logger.error(message, args)
}
async info (message: string, ...args: any[]): Promise<void> {
this.logger.info(message, args)
}
end (): void {
+3 -2
View File
@@ -31,7 +31,7 @@ export interface MeasureLogger {
*/
export interface MeasureContext {
// Create a child metrics context
newChild: (name: string, params: Record<string, ParamType>) => MeasureContext
newChild: (name: string, params: Record<string, ParamType>, logger?: MeasureLogger) => MeasureContext
with: <T>(name: string, params: Record<string, ParamType>, op: (ctx: MeasureContext) => T | Promise<T>) => Promise<T>
@@ -40,7 +40,8 @@ export interface MeasureContext {
measure: (name: string, value: number) => void
// Capture error
error: (err: Error | string | any) => Promise<void>
error: (message: string, ...args: any[]) => Promise<void>
info: (message: string, ...args: any[]) => Promise<void>
// Mark current context as complete
// If no value is passed, time difference will be used.