Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UBERF-7532: Bulk operations for triggers #6023

Merged
merged 2 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions dev/prod/src/platform-dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
// limitations under the License.
//

import { addLocation } from '@hcengineering/platform'
import { devModelId } from '@hcengineering/devmodel'
import { PresentationClientHook } from '@hcengineering/devmodel-resources'
import login from '@hcengineering/login'
import { setMetadata } from '@hcengineering/platform'
import devmodel, { devModelId } from '@hcengineering/devmodel'
import client from '@hcengineering/client'
import { addLocation, setMetadata } from '@hcengineering/platform'
import presentation from '@hcengineering/presentation'

export function configurePlatformDevServer() {
console.log('Use Endpoint override:', process.env.LOGIN_ENDPOINT)
Expand All @@ -28,6 +28,6 @@ export function configurePlatformDevServer() {
}

function enableDevModel() {
setMetadata(client.metadata.ClientHook, devmodel.hook.Hook)
setMetadata(presentation.metadata.ClientHook, new PresentationClientHook())
addLocation(devModelId, () => import(/* webpackChunkName: "devmodel" */ '@hcengineering/devmodel-resources'))
}
3 changes: 0 additions & 3 deletions packages/core/src/__tests__/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,6 @@ describe('client', () => {
clean: async (domain: Domain, docs: Ref<Doc>[]) => {},
loadModel: async (last: Timestamp) => clone(txes),
getAccount: async () => null as unknown as Account,
measure: async () => {
return async () => ({ time: 0, serverTime: 0 })
},
sendForceClose: async () => {}
}
}
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/__tests__/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ export async function connect (handler: (tx: Tx) => void): Promise<ClientConnect
clean: async (domain: Domain, docs: Ref<Doc>[]) => {},
loadModel: async (last: Timestamp) => txes,
getAccount: async () => null as unknown as Account,
measure: async () => async () => ({ time: 0, serverTime: 0 }),
sendForceClose: async () => {}
}
}
17 changes: 2 additions & 15 deletions packages/core/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,10 @@ 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 MeasureClient {
export interface AccountClient extends Client {
getAccount: () => Promise<Account>
}

Expand Down Expand Up @@ -97,11 +90,9 @@ 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, MeasureClient {
class ClientImpl implements AccountClient, BackupClient {
notify?: (...tx: Tx[]) => void
hierarchy!: Hierarchy
model!: ModelDb
Expand Down Expand Up @@ -163,10 +154,6 @@ class ClientImpl implements AccountClient, BackupClient, MeasureClient {
return result
}

async measure (operationName: string): Promise<MeasureDoneOperation> {
return await this.conn.measure(operationName)
}

async updateFromRemote (...tx: Tx[]): Promise<void> {
for (const t of tx) {
try {
Expand Down
48 changes: 30 additions & 18 deletions packages/core/src/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,8 +312,8 @@ export class TxOperations implements Omit<Client, 'notify'> {
return this.removeDoc(doc._class, doc.space, doc._id)
}

apply (scope: string): ApplyOperations {
return new ApplyOperations(this, scope)
apply (scope: string, measure?: string): ApplyOperations {
return new ApplyOperations(this, scope, measure)
}

async diffUpdate<T extends Doc = Doc>(
Expand Down Expand Up @@ -423,6 +423,12 @@ export class TxOperations implements Omit<Client, 'notify'> {
}
}

export interface CommitResult {
result: boolean
time: number
serverTime: number
}

/**
* @public
*
Expand All @@ -436,7 +442,8 @@ export class ApplyOperations extends TxOperations {
notMatches: DocumentClassQuery<Doc>[] = []
constructor (
readonly ops: TxOperations,
readonly scope: string
readonly scope: string,
readonly measureName?: string
) {
const txClient: Client = {
getHierarchy: () => ops.client.getHierarchy(),
Expand Down Expand Up @@ -465,23 +472,28 @@ export class ApplyOperations extends TxOperations {
return this
}

async commit (notify: boolean = true, extraNotify: Ref<Class<Doc>>[] = []): Promise<boolean> {
async commit (notify: boolean = true, extraNotify: Ref<Class<Doc>>[] = []): Promise<CommitResult> {
if (this.txes.length > 0) {
return (
await ((await this.ops.tx(
this.ops.txFactory.createTxApplyIf(
core.space.Tx,
this.scope,
this.matches,
this.notMatches,
this.txes,
notify,
extraNotify
)
)) as Promise<TxApplyResult>)
).success
const st = Date.now()
const result = await ((await this.ops.tx(
this.ops.txFactory.createTxApplyIf(
core.space.Tx,
this.scope,
this.matches,
this.notMatches,
this.txes,
this.measureName,
notify,
extraNotify
)
)) as Promise<TxApplyResult>)
return {
result: result.success,
time: Date.now() - st,
serverTime: result.serverTime
}
}
return true
return { result: true, time: 0, serverTime: 0 }
}
}

Expand Down
9 changes: 5 additions & 4 deletions packages/core/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,15 @@ export interface StorageIterator {
close: (ctx: MeasureContext) => Promise<void>
}

export type BroadcastTargets = Record<string, (tx: Tx) => string[] | undefined>

export interface SessionOperationContext {
ctx: MeasureContext
// A parts of derived data to deal with after operation will be complete
derived: {
derived: Tx[]
target?: string[]
}[]

txes: Tx[]
targets: BroadcastTargets // A set of broadcast filters if required
}
with: <T>(
name: string,
params: ParamsType,
Expand Down
8 changes: 7 additions & 1 deletion packages/core/src/tx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ import type {
Space,
Timestamp
} from './classes'
import { clone } from './clone'
import core from './component'
import { setObjectValue } from './objvalue'
import { _getOperator } from './operator'
import { _toDoc } from './proxy'
import type { DocumentQuery, TxResult } from './storage'
import { generateId } from './utils'
import { clone } from './clone'

/**
* @public
Expand Down Expand Up @@ -137,10 +137,14 @@ export interface TxApplyIf extends Tx {

// If passed, will send WorkspaceEvent.BulkUpdate event with list of classes to update
extraNotify?: Ref<Class<Doc>>[]

// If defined will go into a separate measure section
measureName?: string
}

export interface TxApplyResult {
success: boolean
serverTime: number
}

/**
Expand Down Expand Up @@ -618,6 +622,7 @@ export class TxFactory {
match: DocumentClassQuery<Doc>[],
notMatch: DocumentClassQuery<Doc>[],
txes: TxCUD<Doc>[],
measureName: string | undefined,
notify: boolean = true,
extraNotify: Ref<Class<Doc>>[] = [],
modifiedOn?: Timestamp,
Expand All @@ -634,6 +639,7 @@ export class TxFactory {
match,
notMatch,
txes,
measureName,
notify,
extraNotify
}
Expand Down
12 changes: 3 additions & 9 deletions packages/presentation/src/pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import {
type FindOptions,
type FindResult,
type Hierarchy,
type MeasureClient,
type MeasureDoneOperation,
type ModelDb,
type Ref,
type SearchOptions,
Expand Down Expand Up @@ -65,7 +63,7 @@ export type PresentationMiddlewareCreator = (client: Client, next?: Presentation
/**
* @public
*/
export interface PresentationPipeline extends MeasureClient, Exclude<PresentationMiddleware, 'next'> {
export interface PresentationPipeline extends Client, Exclude<PresentationMiddleware, 'next'> {
close: () => Promise<void>
}

Expand All @@ -75,7 +73,7 @@ export interface PresentationPipeline extends MeasureClient, Exclude<Presentatio
export class PresentationPipelineImpl implements PresentationPipeline {
private head: PresentationMiddleware | undefined

private constructor (readonly client: MeasureClient) {}
private constructor (readonly client: Client) {}

getHierarchy (): Hierarchy {
return this.client.getHierarchy()
Expand All @@ -89,11 +87,7 @@ export class PresentationPipelineImpl implements PresentationPipeline {
await this.head?.notifyTx(...tx)
}

async measure (operationName: string): Promise<MeasureDoneOperation> {
return await this.client.measure(operationName)
}

static create (client: MeasureClient, constructors: PresentationMiddlewareCreator[]): PresentationPipeline {
static create (client: Client, constructors: PresentationMiddlewareCreator[]): PresentationPipeline {
const pipeline = new PresentationPipelineImpl(client)
pipeline.head = pipeline.buildChain(constructors)
return pipeline
Expand Down
51 changes: 45 additions & 6 deletions packages/presentation/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,64 @@
// limitations under the License.
//

import { type Mixin, type Class, type Ref } from '@hcengineering/core'
import {
type Class,
type Client,
type Doc,
type DocumentQuery,
type FindOptions,
type FindResult,
type Mixin,
type Ref,
type SearchOptions,
type SearchQuery,
type SearchResult,
type Tx,
type TxResult,
type WithLookup
} from '@hcengineering/core'
import type { Asset, IntlString, Metadata, Plugin, StatusCode } from '@hcengineering/platform'
import { plugin } from '@hcengineering/platform'
import { type ComponentExtensionId } from '@hcengineering/ui'
import { type PresentationMiddlewareFactory } from './pipeline'
import type { PreviewConfig } from './preview'
import {
type ComponentPointExtension,
type DocRules,
type DocCreateExtension,
type DocRules,
type FilePreviewExtension,
type ObjectSearchCategory,
type InstantTransactions
type InstantTransactions,
type ObjectSearchCategory
} from './types'
import type { PreviewConfig } from './preview'

/**
* @public
*/
export const presentationId = 'presentation' as Plugin

/**
* @public
*/
export interface ClientHook {
findAll: <T extends Doc>(
client: Client,
_class: Ref<Class<T>>,
query: DocumentQuery<T>,
options?: FindOptions<T>
) => Promise<FindResult<T>>

findOne: <T extends Doc>(
client: Client,
_class: Ref<Class<T>>,
query: DocumentQuery<T>,
options?: FindOptions<T>
) => Promise<WithLookup<T> | undefined>

tx: (client: Client, tx: Tx) => Promise<TxResult>

searchFulltext: (client: Client, query: SearchQuery, options: SearchOptions) => Promise<SearchResult>
}

export default plugin(presentationId, {
class: {
ObjectSearchCategory: '' as Ref<Class<ObjectSearchCategory>>,
Expand Down Expand Up @@ -95,7 +133,8 @@ export default plugin(presentationId, {
CollaboratorApiUrl: '' as Metadata<string>,
Token: '' as Metadata<string>,
FrontUrl: '' as Asset,
PreviewConfig: '' as Metadata<PreviewConfig | undefined>
PreviewConfig: '' as Metadata<PreviewConfig | undefined>,
ClientHook: '' as Metadata<ClientHook>
},
status: {
FileTooLarge: '' as StatusCode
Expand Down
Loading