UBERF-7489: Fix various performance issues (#5983)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2024-07-03 15:16:04 +07:00 committed by GitHub
parent 9d3ce44ce7
commit d5a83391c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 51 additions and 37 deletions

View File

@ -21,7 +21,6 @@ import core, {
concatLink, concatLink,
getCurrentAccount, getCurrentAccount,
reduceCalls, reduceCalls,
type TxApplyIf,
type AnyAttribute, type AnyAttribute,
type ArrOf, type ArrOf,
type AttachedDoc, type AttachedDoc,
@ -46,10 +45,11 @@ import core, {
type SearchResult, type SearchResult,
type Space, type Space,
type Tx, type Tx,
type TxApplyIf,
type TxCUD,
type TxResult, type TxResult,
type TypeAny, type TypeAny,
type WithLookup, type WithLookup
type TxCUD
} from '@hcengineering/core' } from '@hcengineering/core'
import { getMetadata, getResource } from '@hcengineering/platform' import { getMetadata, getResource } from '@hcengineering/platform'
import { LiveQuery as LQ } from '@hcengineering/query' import { LiveQuery as LQ } from '@hcengineering/query'
@ -57,13 +57,14 @@ import { getRawCurrentLocation, workspaceId, type AnyComponent, type AnySvelteCo
import view, { type AttributeCategory, type AttributeEditor } from '@hcengineering/view' import view, { type AttributeCategory, type AttributeEditor } from '@hcengineering/view'
import { deepEqual } from 'fast-equals' import { deepEqual } from 'fast-equals'
import { onDestroy } from 'svelte' import { onDestroy } from 'svelte'
import { type Writable, get, writable } from 'svelte/store' import { get, writable, type Writable } from 'svelte/store'
import { type KeyedAttribute } from '..' import { type KeyedAttribute } from '..'
import { OptimizeQueryMiddleware, PresentationPipelineImpl, type PresentationPipeline } from './pipeline' import { OptimizeQueryMiddleware, PresentationPipelineImpl, type PresentationPipeline } from './pipeline'
import plugin from './plugin' import plugin from './plugin'
export { reduceCalls } from '@hcengineering/core' export { reduceCalls } from '@hcengineering/core'
let liveQuery: LQ let liveQuery: LQ
let rawLiveQuery: LQ
let client: TxOperations & MeasureClient & OptimisticTxes let client: TxOperations & MeasureClient & OptimisticTxes
let pipeline: PresentationPipeline let pipeline: PresentationPipeline
@ -76,6 +77,10 @@ export function addTxListener (l: (tx: Tx) => void): void {
txListeners.push(l) txListeners.push(l)
} }
export function getRawLiveQuery (): LQ {
return rawLiveQuery
}
/** /**
* @public * @public
*/ */
@ -144,6 +149,8 @@ class UIClient extends TxOperations implements Client, MeasureClient, Optimistic
await liveQuery.tx(...tx) await liveQuery.tx(...tx)
await rawLiveQuery.tx(...tx)
txListeners.forEach((it) => { txListeners.forEach((it) => {
it(...tx) it(...tx)
}) })
@ -250,17 +257,24 @@ export async function setClient (_client: MeasureClient): Promise<void> {
if (liveQuery !== undefined) { if (liveQuery !== undefined) {
await liveQuery.close() await liveQuery.close()
} }
if (rawLiveQuery !== undefined) {
await rawLiveQuery.close()
}
if (pipeline !== undefined) { if (pipeline !== undefined) {
await pipeline.close() await pipeline.close()
} }
const needRefresh = liveQuery !== undefined
rawLiveQuery = new LQ(_client)
const factories = await _client.findAll(plugin.class.PresentationMiddlewareFactory, {}) const factories = await _client.findAll(plugin.class.PresentationMiddlewareFactory, {})
const promises = factories.map(async (it) => await getResource(it.createPresentationMiddleware)) const promises = factories.map(async (it) => await getResource(it.createPresentationMiddleware))
const creators = await Promise.all(promises) const creators = await Promise.all(promises)
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
pipeline = PresentationPipelineImpl.create(_client, [OptimizeQueryMiddleware.create, ...creators]) pipeline = PresentationPipelineImpl.create(_client, [OptimizeQueryMiddleware.create, ...creators])
const needRefresh = liveQuery !== undefined
liveQuery = new LQ(pipeline) liveQuery = new LQ(pipeline)
const uiClient = new UIClient(pipeline, liveQuery) const uiClient = new UIClient(pipeline, liveQuery)
client = uiClient client = uiClient

View File

@ -406,7 +406,27 @@
$: projectPreferences.query(tracker.class.ProjectTargetPreference, {}, (res) => { $: projectPreferences.query(tracker.class.ProjectTargetPreference, {}, (res) => {
preferences = res preferences = res
}) })
$: spacePreferences = preferences.find((it) => it.attachedTo === _space)
async function updateCurrentProjectPref (currentProject: Ref<Project>): Promise<void> {
const spacePreferences = await client.findOne(tracker.class.ProjectTargetPreference, { attachedTo: currentProject })
if (spacePreferences === undefined) {
await client.createDoc(tracker.class.ProjectTargetPreference, currentProject, {
attachedTo: currentProject,
props: [],
usedOn: Date.now()
})
} else {
if (spacePreferences.usedOn + 60 * 1000 < Date.now()) {
await client.update(spacePreferences, {
usedOn: Date.now()
})
}
}
}
$: if (_space !== undefined) {
void updateCurrentProjectPref(_space)
}
async function createIssue (): Promise<void> { async function createIssue (): Promise<void> {
const _id: Ref<Issue> = generateId() const _id: Ref<Issue> = generateId()
@ -713,24 +733,6 @@
originalIssue, originalIssue,
preferences preferences
} }
function updateCurrentProjectPref (currentProject: Ref<Project>): void {
if (spacePreferences === undefined) {
void client.createDoc(tracker.class.ProjectTargetPreference, currentProject, {
attachedTo: currentProject,
props: [],
usedOn: Date.now()
})
} else {
void client.update(spacePreferences, {
usedOn: Date.now()
})
}
}
$: if (_space !== undefined) {
updateCurrentProjectPref(_space)
}
</script> </script>
<FocusHandler {manager} /> <FocusHandler {manager} />

View File

@ -18,12 +18,15 @@ import { Analytics } from '@hcengineering/analytics'
import core, { import core, {
AccountRole, AccountRole,
ClassifierKind, ClassifierKind,
DocManager,
Hierarchy, Hierarchy,
SortingOrder,
TxProcessor, TxProcessor,
getCurrentAccount, getCurrentAccount,
getObjectValue, getObjectValue,
type Account, type Account,
type AggregateValue, type AggregateValue,
type AnyAttribute,
type AttachedDoc, type AttachedDoc,
type CategoryType, type CategoryType,
type Class, type Class,
@ -42,6 +45,7 @@ import core, {
type ReverseLookup, type ReverseLookup,
type ReverseLookups, type ReverseLookups,
type Space, type Space,
type Tx,
type TxCUD, type TxCUD,
type TxCollectionCUD, type TxCollectionCUD,
type TxCreateDoc, type TxCreateDoc,
@ -50,11 +54,7 @@ import core, {
type TxUpdateDoc, type TxUpdateDoc,
type TypeAny, type TypeAny,
type TypedSpace, type TypedSpace,
type WithLookup, type WithLookup
type AnyAttribute,
DocManager,
SortingOrder,
type Tx
} from '@hcengineering/core' } from '@hcengineering/core'
import { type Restrictions } from '@hcengineering/guest' import { type Restrictions } from '@hcengineering/guest'
import type { Asset, IntlString } from '@hcengineering/platform' import type { Asset, IntlString } from '@hcengineering/platform'
@ -64,11 +64,11 @@ import {
getAttributePresenterClass, getAttributePresenterClass,
getClient, getClient,
getFiltredKeys, getFiltredKeys,
getRawLiveQuery,
hasResource, hasResource,
isAdminUser, isAdminUser,
type KeyedAttribute type KeyedAttribute
} from '@hcengineering/presentation' } from '@hcengineering/presentation'
import { LiveQuery } from '@hcengineering/query'
import { type CollaborationUser } from '@hcengineering/text-editor' import { type CollaborationUser } from '@hcengineering/text-editor'
import { import {
ErrorPresenter, ErrorPresenter,
@ -85,7 +85,6 @@ import {
type Location type Location
} from '@hcengineering/ui' } from '@hcengineering/ui'
import view, { import view, {
type IAggregationManager,
AttributeCategoryOrder, AttributeCategoryOrder,
type AttributeCategory, type AttributeCategory,
type AttributeModel, type AttributeModel,
@ -93,9 +92,10 @@ import view, {
type BuildModelKey, type BuildModelKey,
type BuildModelOptions, type BuildModelOptions,
type CollectionPresenter, type CollectionPresenter,
type IAggregationManager,
type LinkIdProvider,
type Viewlet, type Viewlet,
type ViewletDescriptor, type ViewletDescriptor
type LinkIdProvider
} from '@hcengineering/view' } from '@hcengineering/view'
import contact, { getName, type Contact, type PersonAccount } from '@hcengineering/contact' import contact, { getName, type Contact, type PersonAccount } from '@hcengineering/contact'
@ -119,7 +119,6 @@ export class AggregationManager<T extends Doc> implements IAggregationManager<T>
docs: T[] | undefined docs: T[] | undefined
mgr: DocManager<T> | Promise<DocManager<T>> | undefined mgr: DocManager<T> | Promise<DocManager<T>> | undefined
query: (() => void) | undefined query: (() => void) | undefined
lq: LiveQuery
lqCallback: () => void lqCallback: () => void
private readonly setStore: (manager: DocManager<T>) => void private readonly setStore: (manager: DocManager<T>) => void
private readonly filter: (doc: T, target: T) => boolean private readonly filter: (doc: T, target: T) => boolean
@ -132,7 +131,6 @@ export class AggregationManager<T extends Doc> implements IAggregationManager<T>
categorizingFunc: (doc: T, target: T) => boolean, categorizingFunc: (doc: T, target: T) => boolean,
_class: Ref<Class<T>> _class: Ref<Class<T>>
) { ) {
this.lq = new LiveQuery(client)
this.lqCallback = lqCallback ?? (() => {}) this.lqCallback = lqCallback ?? (() => {})
this.setStore = setStore this.setStore = setStore
this.filter = categorizingFunc this.filter = categorizingFunc
@ -158,7 +156,7 @@ export class AggregationManager<T extends Doc> implements IAggregationManager<T>
return this.mgr return this.mgr
} }
this.mgr = new Promise<DocManager<T>>((resolve) => { this.mgr = new Promise<DocManager<T>>((resolve) => {
this.query = this.lq.query( this.query = getRawLiveQuery().query(
this._class, this._class,
{}, {},
(res) => { (res) => {
@ -187,7 +185,7 @@ export class AggregationManager<T extends Doc> implements IAggregationManager<T>
} }
async notifyTx (...tx: Tx[]): Promise<void> { async notifyTx (...tx: Tx[]): Promise<void> {
await this.lq.tx(...tx) // This is intentional
} }
getAttrClass (): Ref<Class<T>> { getAttrClass (): Ref<Class<T>> {