From c35db5c2a232b0855b455739e6d87ae02fa7126d Mon Sep 17 00:00:00 2001 From: nathan Date: Thu, 2 Mar 2023 19:47:37 +0800 Subject: [PATCH] fix: high cpu usage --- frontend/appflowy_tauri/src-tauri/src/init.rs | 2 +- .../TestApiButton/DatabaseTestHelper.ts | 70 ++++++++--- .../components/TestApiButton/TestAPI.tsx | 4 + .../components/TestApiButton/TestGrid.tsx | 115 ++++++++++++++++-- .../effects/database/cell/cell_cache.ts | 17 +-- .../effects/database/cell/cell_controller.ts | 64 +++++----- .../effects/database/cell/cell_observer.ts | 25 ++-- .../database/cell/controller_builder.ts | 63 ++-------- .../effects/database/cell/data_parser.ts | 6 +- .../effects/database/cell/data_persistence.ts | 2 - .../database/cell/select_option_bd_svc.ts | 3 +- .../effects/database/database_controller.ts | 8 +- .../database/field/field_controller.ts | 49 ++++---- .../effects/database/field/field_observer.ts | 8 +- .../field/type_option/type_option_context.ts | 2 +- .../type_option/type_option_controller.ts | 24 +++- .../database/notifications/observer.ts | 4 +- .../stores/effects/database/row/row_cache.ts | 86 +++++++------ .../database/view/database_view_cache.ts | 3 +- .../database/view/view_row_observer.ts | 2 +- .../src/ts_event/event_template.tera | 1 + .../rust-lib/flowy-database/src/manager.rs | 46 ++++--- .../src/services/database/database_editor.rs | 4 +- .../src/services/database_view/editor.rs | 8 +- .../services/database_view/editor_manager.rs | 6 +- .../field/type_options/type_option_cell.rs | 1 + .../persistence/rev_sqlite/block_impl.rs | 33 +++-- .../persistence/rev_sqlite/database_impl.rs | 15 ++- .../persistence/rev_sqlite/view_impl.rs | 33 +++-- .../src/services/sort/controller.rs | 11 +- .../rev_sqlite/folder_rev_sqlite.rs | 15 ++- frontend/rust-lib/flowy-task/src/scheduler.rs | 49 +++----- 32 files changed, 454 insertions(+), 325 deletions(-) diff --git a/frontend/appflowy_tauri/src-tauri/src/init.rs b/frontend/appflowy_tauri/src-tauri/src/init.rs index a9d22eefbf..98ab6d5376 100644 --- a/frontend/appflowy_tauri/src-tauri/src/init.rs +++ b/frontend/appflowy_tauri/src-tauri/src/init.rs @@ -10,7 +10,7 @@ pub fn init_flowy_core() -> AppFlowyCore { } data_path.push("data"); - std::env::set_var("RUST_LOG", "debug"); + std::env::set_var("RUST_LOG", "trace"); let server_config = get_client_server_configuration().unwrap(); let config = AppFlowyCoreConfig::new( data_path.to_str().unwrap(), diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/DatabaseTestHelper.ts b/frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/DatabaseTestHelper.ts index 4a79bc16e0..1eecc6f70a 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/DatabaseTestHelper.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/DatabaseTestHelper.ts @@ -11,7 +11,6 @@ import { SelectOptionCellController, TextCellController, } from '../../stores/effects/database/cell/controller_builder'; -import assert from 'assert'; import { None, Option, Some } from 'ts-results'; import { TypeOptionBackendService } from '../../stores/effects/database/field/type_option/type_option_bd_svc'; import { DatabaseBackendService } from '../../stores/effects/database/database_bd_svc'; @@ -29,9 +28,16 @@ export async function openTestDatabase(viewId: string): Promise result.unwrap()); - cellController.subscribeChanged({ +export async function assertTextCell( + fieldId: string, + rowInfo: RowInfo, + databaseController: DatabaseController, + expectedContent: string +) { + const cellController = await makeTextCellController(fieldId, rowInfo, databaseController).then((result) => + result.unwrap() + ); + await cellController.subscribeChanged({ onCellChanged: (value) => { const cellContent = value.unwrap(); if (cellContent !== expectedContent) { @@ -39,55 +45,78 @@ export async function assertTextCell(rowInfo: RowInfo, databaseController: Datab } }, }); - cellController.getCellData(); + await cellController.getCellData(); } -export async function editTextCell(rowInfo: RowInfo, databaseController: DatabaseController, content: string) { - const cellController = await makeTextCellController(rowInfo, databaseController).then((result) => result.unwrap()); +export async function editTextCell( + fieldId: string, + rowInfo: RowInfo, + databaseController: DatabaseController, + content: string +) { + const cellController = await makeTextCellController(fieldId, rowInfo, databaseController).then((result) => + result.unwrap() + ); await cellController.saveCellData(content); } export async function makeTextCellController( + fieldId: string, rowInfo: RowInfo, databaseController: DatabaseController ): Promise> { - const builder = await makeCellControllerBuilder(rowInfo, FieldType.RichText, databaseController).then((result) => - result.unwrap() + const builder = await makeCellControllerBuilder(fieldId, rowInfo, FieldType.RichText, databaseController).then( + (result) => result.unwrap() ); return Some(builder.build() as TextCellController); } export async function makeNumberCellController( + fieldId: string, rowInfo: RowInfo, databaseController: DatabaseController ): Promise> { - const builder = await makeCellControllerBuilder(rowInfo, FieldType.Number, databaseController).then((result) => - result.unwrap() + const builder = await makeCellControllerBuilder(fieldId, rowInfo, FieldType.Number, databaseController).then( + (result) => result.unwrap() ); return Some(builder.build() as NumberCellController); } export async function makeSingleSelectCellController( + fieldId: string, rowInfo: RowInfo, databaseController: DatabaseController ): Promise> { - const builder = await makeCellControllerBuilder(rowInfo, FieldType.SingleSelect, databaseController).then((result) => - result.unwrap() + const builder = await makeCellControllerBuilder(fieldId, rowInfo, FieldType.SingleSelect, databaseController).then( + (result) => result.unwrap() + ); + return Some(builder.build() as SelectOptionCellController); +} + +export async function makeMultiSelectCellController( + fieldId: string, + rowInfo: RowInfo, + databaseController: DatabaseController +): Promise> { + const builder = await makeCellControllerBuilder(fieldId, rowInfo, FieldType.MultiSelect, databaseController).then( + (result) => result.unwrap() ); return Some(builder.build() as SelectOptionCellController); } export async function makeDateCellController( + fieldId: string, rowInfo: RowInfo, databaseController: DatabaseController ): Promise> { - const builder = await makeCellControllerBuilder(rowInfo, FieldType.DateTime, databaseController).then((result) => - result.unwrap() + const builder = await makeCellControllerBuilder(fieldId, rowInfo, FieldType.DateTime, databaseController).then( + (result) => result.unwrap() ); return Some(builder.build() as DateCellController); } export async function makeCellControllerBuilder( + fieldId: string, rowInfo: RowInfo, fieldType: FieldType, databaseController: DatabaseController @@ -99,7 +128,7 @@ export async function makeCellControllerBuilder( const cellByFieldId = await rowController.loadCells(); for (const cellIdentifier of cellByFieldId.values()) { const builder = new CellControllerBuilder(cellIdentifier, cellCache, fieldController); - if (cellIdentifier.fieldType === fieldType) { + if (cellIdentifier.fieldId === fieldId) { return Some(builder); } } @@ -107,6 +136,15 @@ export async function makeCellControllerBuilder( return None; } +export function findFirstFieldInfoWithFieldType(rowInfo: RowInfo, fieldType: FieldType) { + const fieldInfo = rowInfo.fieldInfos.find((element) => element.field.field_type === fieldType); + if (fieldInfo === undefined) { + return None; + } else { + return Some(fieldInfo); + } +} + export async function assertFieldName(viewId: string, fieldId: string, fieldType: FieldType, expected: string) { const svc = new TypeOptionBackendService(viewId); const typeOptionPB = await svc.getTypeOption(fieldId, fieldType).then((result) => result.unwrap()); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/TestAPI.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/TestAPI.tsx index 0f9a1fb9d1..b5354da161 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/TestAPI.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/TestAPI.tsx @@ -10,6 +10,8 @@ import { TestEditCell, TestEditField, TestGetSingleSelectFieldData, + TestSwitchFromMultiSelectToText, + TestSwitchFromSingleSelectToNumber, } from './TestGrid'; export const TestAPI = () => { @@ -26,6 +28,8 @@ export const TestAPI = () => { + + ); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/TestGrid.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/TestGrid.tsx index d0ccbfab47..b4099252ce 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/TestGrid.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/TestGrid.tsx @@ -1,6 +1,8 @@ import React from 'react'; import { FieldType, + NumberFormat, + NumberTypeOptionPB, SelectOptionCellDataPB, SingleSelectTypeOptionPB, ViewLayoutTypePB, @@ -13,7 +15,10 @@ import { assertTextCell, createTestDatabaseView, editTextCell, + findFirstFieldInfoWithFieldType, + makeMultiSelectCellController, makeSingleSelectCellController, + makeTextCellController, openTestDatabase, } from './DatabaseTestHelper'; import { @@ -23,7 +28,10 @@ import { import { TypeOptionController } from '../../stores/effects/database/field/type_option/type_option_controller'; import { None, Some } from 'ts-results'; import { RowBackendService } from '../../stores/effects/database/row/row_bd_svc'; -import { makeSingleSelectTypeOptionContext } from '../../stores/effects/database/field/type_option/type_option_context'; +import { + makeNumberTypeOptionContext, + makeSingleSelectTypeOptionContext, +} from '../../stores/effects/database/field/type_option/type_option_context'; export const TestCreateGrid = () => { async function createBuildInGrid() { @@ -33,11 +41,11 @@ export const TestCreateGrid = () => { onViewChanged: (databasePB) => { Log.debug('Did receive database:' + databasePB); }, - onRowsChanged: async (rows) => { - if (rows.length !== 3) { - throw Error('Expected number of rows is 3, but receive ' + rows.length + view.id); - } - }, + // onRowsChanged: async (rows) => { + // if (rows.length !== 3) { + // throw Error('Expected number of rows is 3, but receive ' + rows.length); + // } + // }, onFieldsChanged: (fields) => { if (fields.length !== 3) { throw Error('Expected number of fields is 3, but receive ' + fields.length); @@ -59,8 +67,9 @@ export const TestEditCell = () => { for (const [index, row] of databaseController.databaseViewCache.rowInfos.entries()) { const cellContent = index.toString(); - await editTextCell(row, databaseController, cellContent); - await assertTextCell(row, databaseController, cellContent); + const fieldInfo = findFirstFieldInfoWithFieldType(row, FieldType.RichText).unwrap(); + await editTextCell(fieldInfo.field.id, row, databaseController, cellContent); + await assertTextCell(fieldInfo.field.id, row, databaseController, cellContent); } } @@ -111,10 +120,11 @@ export const TestCreateSelectOptionInCell = () => { await databaseController.open().then((result) => result.unwrap()); for (const [index, row] of databaseController.databaseViewCache.rowInfos.entries()) { if (index === 0) { - const cellController = await makeSingleSelectCellController(row, databaseController).then((result) => - result.unwrap() + const fieldInfo = findFirstFieldInfoWithFieldType(row, FieldType.SingleSelect).unwrap(); + const cellController = await makeSingleSelectCellController(fieldInfo.field.id, row, databaseController).then( + (result) => result.unwrap() ); - cellController.subscribeChanged({ + await cellController.subscribeChanged({ onCellChanged: (value) => { const option: SelectOptionCellDataPB = value.unwrap(); console.log(option); @@ -167,6 +177,89 @@ export const TestGetSingleSelectFieldData = () => { return TestButton('Test get single-select column data', testGetSingleSelectFieldData); }; +export const TestSwitchFromSingleSelectToNumber = () => { + async function testSwitchFromSingleSelectToNumber() { + const view = await createTestDatabaseView(ViewLayoutTypePB.Grid); + const databaseController = await openTestDatabase(view.id); + await databaseController.open().then((result) => result.unwrap()); + + // Find the single select column + const singleSelect = databaseController.fieldController.fieldInfos.find( + (fieldInfo) => fieldInfo.field.field_type === FieldType.SingleSelect + )!; + const typeOptionController = new TypeOptionController(view.id, Some(singleSelect)); + await typeOptionController.switchToField(FieldType.Number); + + // Check the number type option + const numberTypeOptionContext = makeNumberTypeOptionContext(typeOptionController); + const numberTypeOption: NumberTypeOptionPB = await numberTypeOptionContext + .getTypeOption() + .then((result) => result.unwrap()); + const format: NumberFormat = numberTypeOption.format; + if (format !== NumberFormat.Num) { + throw Error('The default format should be number'); + } + + await databaseController.dispose(); + } + + return TestButton('Test switch from single-select to number column', testSwitchFromSingleSelectToNumber); +}; + +export const TestSwitchFromMultiSelectToText = () => { + async function testSwitchFromMultiSelectToRichText() { + const view = await createTestDatabaseView(ViewLayoutTypePB.Grid); + const databaseController = await openTestDatabase(view.id); + await databaseController.open().then((result) => result.unwrap()); + + // Create multi-select field + const typeOptionController = new TypeOptionController(view.id, None, FieldType.MultiSelect); + await typeOptionController.initialize(); + + // Insert options to first row + const row = databaseController.databaseViewCache.rowInfos[0]; + const multiSelectField = typeOptionController.getFieldInfo(); + // const multiSelectField = findFirstFieldInfoWithFieldType(row, FieldType.MultiSelect).unwrap(); + const selectOptionCellController = await makeMultiSelectCellController( + multiSelectField.field.id, + row, + databaseController + ).then((result) => result.unwrap()); + const backendSvc = new SelectOptionCellBackendService(selectOptionCellController.cellIdentifier); + await backendSvc.createOption({ name: 'A' }); + await backendSvc.createOption({ name: 'B' }); + await backendSvc.createOption({ name: 'C' }); + + const selectOptionCellData = await selectOptionCellController.getCellData().then((result) => result.unwrap()); + if (selectOptionCellData.options.length !== 3) { + throw Error('The options should equal to 3'); + } + + if (selectOptionCellData.select_options.length !== 3) { + throw Error('The selected options should equal to 3'); + } + await selectOptionCellController.dispose(); + + // Switch to RichText field type + await typeOptionController.switchToField(FieldType.RichText).then((result) => result.unwrap()); + if (typeOptionController.fieldType !== FieldType.RichText) { + throw Error('The field type should be text'); + } + + const textCellController = await makeTextCellController(multiSelectField.field.id, row, databaseController).then( + (result) => result.unwrap() + ); + const cellContent = await textCellController.getCellData(); + if (cellContent.unwrap() !== 'A,B,C') { + throw Error('The cell content should be A,B,C, but receive: ' + cellContent.unwrap()); + } + + await databaseController.dispose(); + } + + return TestButton('Test switch from multi-select to text column', testSwitchFromMultiSelectToRichText); +}; + export const TestEditField = () => { async function testEditField() { const view = await createTestDatabaseView(ViewLayoutTypePB.Grid); diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_cache.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_cache.ts index 27099145c3..a19ad13d27 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_cache.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_cache.ts @@ -35,18 +35,19 @@ export class CellCache { }; get(key: CellCacheKey): Option { - const inner = this.cellDataByFieldId.get(key.fieldId); - if (inner === undefined) { + const cellDataByRowId = this.cellDataByFieldId.get(key.fieldId); + if (cellDataByRowId === undefined) { return None; } else { - const value = inner.get(key.rowId); - if (typeof value === typeof undefined || typeof value === typeof null) { + const value = cellDataByRowId.get(key.rowId); + if (typeof value === typeof undefined) { return None; } - if (value satisfies T) { - return Some(value as T); - } - return None; + + // if (value satisfies T) { + // return Some(value as T); + // } + return Some(value); } } } diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_controller.ts index c438cd6cc8..6d3050d88a 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_controller.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_controller.ts @@ -8,28 +8,29 @@ import { ChangeNotifier } from '../../../../utils/change_notifier'; import { CellObserver } from './cell_observer'; import { Log } from '../../../../utils/log'; import { Err, None, Ok, Option, Some } from 'ts-results'; +import { DatabaseFieldObserver } from '../field/field_observer'; -export abstract class CellFieldNotifier { - abstract subscribeOnFieldChanged(callback: () => void): void; -} +type Callbacks = { onCellChanged: (value: Option) => void; onFieldChanged?: () => void }; export class CellController { private fieldBackendService: FieldBackendService; private cellDataNotifier: CellDataNotifier>; private cellObserver: CellObserver; private readonly cacheKey: CellCacheKey; + private readonly fieldNotifier: DatabaseFieldObserver; + private subscribeCallbacks?: Callbacks; constructor( public readonly cellIdentifier: CellIdentifier, private readonly cellCache: CellCache, - private readonly fieldNotifier: CellFieldNotifier, private readonly cellDataLoader: CellDataLoader, private readonly cellDataPersistence: CellDataPersistence ) { this.fieldBackendService = new FieldBackendService(cellIdentifier.viewId, cellIdentifier.fieldId); - this.cacheKey = new CellCacheKey(cellIdentifier.rowId, cellIdentifier.fieldId); + this.cacheKey = new CellCacheKey(cellIdentifier.fieldId, cellIdentifier.rowId); this.cellDataNotifier = new CellDataNotifier(cellCache.get(this.cacheKey)); this.cellObserver = new CellObserver(cellIdentifier.rowId, cellIdentifier.fieldId); + this.fieldNotifier = new DatabaseFieldObserver(cellIdentifier.fieldId); void this.cellObserver.subscribe({ /// 1.Listen on user edit event and load the new cell data if needed. /// For example: @@ -40,21 +41,23 @@ export class CellController { await this._loadCellData(); }, }); + + /// 2.Listen on the field event and load the cell data if needed. + void this.fieldNotifier.subscribe({ + onFieldChanged: () => { + this.subscribeCallbacks?.onFieldChanged?.(); + /// reloadOnFieldChanged should be true if you need to load the data when the corresponding field is changed + /// For example: + /// ¥12 -> $12 + if (this.cellDataLoader.reloadOnFieldChanged) { + void this._loadCellData(); + } + }, + }); } - subscribeChanged = (callbacks: { onCellChanged: (value: Option) => void; onFieldChanged?: () => void }) => { - /// 2.Listen on the field event and load the cell data if needed. - this.fieldNotifier.subscribeOnFieldChanged(async () => { - callbacks.onFieldChanged?.(); - - /// reloadOnFieldChanged should be true if you need to load the data when the corresponding field is changed - /// For example: - /// ¥12 -> $12 - if (this.cellDataLoader.reloadOnFieldChanged) { - await this._loadCellData(); - } - }); - + subscribeChanged = async (callbacks: Callbacks) => { + this.subscribeCallbacks = callbacks; this.cellDataNotifier.observer.subscribe((cellData) => { if (cellData !== null) { callbacks.onCellChanged(cellData); @@ -78,21 +81,21 @@ export class CellController { } }; - /// Return the cell data if it exists in the cache - /// If the cell data is not exist, it will load the cell - /// data from the backend and then the [onCellChanged] will - /// get called - getCellData = (): Option => { + /// Return the cell data immediately if it exists in the cache + /// Otherwise, it will load the cell data from the backend. The + /// subscribers of the [onCellChanged] will get noticed + getCellData = async (): Promise> => { const cellData = this.cellCache.get(this.cacheKey); if (cellData.none) { - void this._loadCellData(); + await this._loadCellData(); + return this.cellCache.get(this.cacheKey); } return cellData; }; private _loadCellData = () => { return this.cellDataLoader.loadData().then((result) => { - if (result.ok && result.val !== undefined) { + if (result.ok) { this.cellCache.insert(this.cacheKey, result.val); this.cellDataNotifier.cellData = Some(result.val); } else { @@ -104,19 +107,10 @@ export class CellController { dispose = async () => { await this.cellObserver.unsubscribe(); + await this.fieldNotifier.unsubscribe(); }; } -export class CellFieldNotifierImpl extends CellFieldNotifier { - constructor(private readonly fieldController: FieldController) { - super(); - } - - subscribeOnFieldChanged(callback: () => void): void { - this.fieldController.subscribeOnFieldsChanged(callback); - } -} - class CellDataNotifier extends ChangeNotifier { _cellData: T | null; diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_observer.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_observer.ts index 06409c7ae2..9aa9763a99 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_observer.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_observer.ts @@ -8,24 +8,27 @@ type UpdateCellNotifiedValue = Result; export type CellChangedCallback = (value: UpdateCellNotifiedValue) => void; export class CellObserver { - private _notifier?: ChangeNotifier; - private _listener?: DatabaseNotificationObserver; + private notifier?: ChangeNotifier; + private listener?: DatabaseNotificationObserver; constructor(public readonly rowId: string, public readonly fieldId: string) {} subscribe = async (callbacks: { onCellChanged: CellChangedCallback }) => { - this._notifier = new ChangeNotifier(); - this._notifier?.observer.subscribe(callbacks.onCellChanged); + this.notifier = new ChangeNotifier(); + this.notifier?.observer.subscribe(callbacks.onCellChanged); - this._listener = new DatabaseNotificationObserver({ - viewId: this.rowId + ':' + this.fieldId, + this.listener = new DatabaseNotificationObserver({ + // The rowId combine with fieldId can identifier the cell. + // This format rowId:fieldId is also defined in the backend, + // so don't change this. + id: this.rowId + ':' + this.fieldId, parserHandler: (notification, result) => { switch (notification) { case DatabaseNotification.DidUpdateCell: if (result.ok) { - this._notifier?.notify(Ok.EMPTY); + this.notifier?.notify(Ok.EMPTY); } else { - this._notifier?.notify(result); + this.notifier?.notify(result); } return; default: @@ -33,11 +36,11 @@ export class CellObserver { } }, }); - await this._listener.start(); + await this.listener.start(); }; unsubscribe = async () => { - this._notifier?.unsubscribe(); - await this._listener?.stop(); + this.notifier?.unsubscribe(); + await this.listener?.stop(); }; } diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/controller_builder.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/controller_builder.ts index 584e77394f..9d66dd6ee5 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/controller_builder.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/controller_builder.ts @@ -1,11 +1,6 @@ -import { - DateCellDataPB, - FieldType, - SelectOptionCellDataPB, - URLCellDataPB, -} from '../../../../../services/backend/models/flowy-database'; +import { DateCellDataPB, FieldType, SelectOptionCellDataPB, URLCellDataPB } from '../../../../../services/backend'; import { CellIdentifier } from './cell_bd_svc'; -import { CellController, CellFieldNotifierImpl } from './cell_controller'; +import { CellController } from './cell_controller'; import { CellDataLoader, DateCellDataParser, @@ -34,15 +29,11 @@ export class CalendarData { export type URLCellController = CellController; export class CellControllerBuilder { - _fieldNotifier: CellFieldNotifierImpl; - constructor( public readonly cellIdentifier: CellIdentifier, public readonly cellCache: CellCache, public readonly fieldController: FieldController - ) { - this._fieldNotifier = new CellFieldNotifierImpl(this.fieldController); - } + ) {} /// build = () => { @@ -68,77 +59,41 @@ export class CellControllerBuilder { const loader = new CellDataLoader(this.cellIdentifier, new SelectOptionCellDataParser(), true); const persistence = new TextCellDataPersistence(this.cellIdentifier); - return new CellController( - this.cellIdentifier, - this.cellCache, - this._fieldNotifier, - loader, - persistence - ); + return new CellController(this.cellIdentifier, this.cellCache, loader, persistence); }; makeURLCellController = (): URLCellController => { const loader = new CellDataLoader(this.cellIdentifier, new URLCellDataParser()); const persistence = new TextCellDataPersistence(this.cellIdentifier); - return new CellController( - this.cellIdentifier, - this.cellCache, - this._fieldNotifier, - loader, - persistence - ); + return new CellController(this.cellIdentifier, this.cellCache, loader, persistence); }; makeDateCellController = (): DateCellController => { const loader = new CellDataLoader(this.cellIdentifier, new DateCellDataParser(), true); const persistence = new DateCellDataPersistence(this.cellIdentifier); - return new CellController( - this.cellIdentifier, - this.cellCache, - this._fieldNotifier, - loader, - persistence - ); + return new CellController(this.cellIdentifier, this.cellCache, loader, persistence); }; makeNumberCellController = (): NumberCellController => { const loader = new CellDataLoader(this.cellIdentifier, new StringCellDataParser(), true); const persistence = new TextCellDataPersistence(this.cellIdentifier); - return new CellController( - this.cellIdentifier, - this.cellCache, - this._fieldNotifier, - loader, - persistence - ); + return new CellController(this.cellIdentifier, this.cellCache, loader, persistence); }; makeTextCellController = (): TextCellController => { const loader = new CellDataLoader(this.cellIdentifier, new StringCellDataParser()); const persistence = new TextCellDataPersistence(this.cellIdentifier); - return new CellController( - this.cellIdentifier, - this.cellCache, - this._fieldNotifier, - loader, - persistence - ); + return new CellController(this.cellIdentifier, this.cellCache, loader, persistence); }; makeCheckboxCellController = (): CheckboxCellController => { const loader = new CellDataLoader(this.cellIdentifier, new StringCellDataParser()); const persistence = new TextCellDataPersistence(this.cellIdentifier); - return new CellController( - this.cellIdentifier, - this.cellCache, - this._fieldNotifier, - loader, - persistence - ); + return new CellController(this.cellIdentifier, this.cellCache, loader, persistence); }; } diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/data_parser.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/data_parser.ts index 0fdb978d72..e44867c0df 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/data_parser.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/data_parser.ts @@ -7,11 +7,11 @@ import { Err, Ok } from 'ts-results'; import { Log } from '../../../../utils/log'; abstract class CellDataParser { - abstract parserData(data: Uint8Array): T | undefined; + abstract parserData(data: Uint8Array): T; } class CellDataLoader { - _service = new CellBackendService(); + private service = new CellBackendService(); constructor( readonly cellId: CellIdentifier, @@ -20,7 +20,7 @@ class CellDataLoader { ) {} loadData = async () => { - const result = await this._service.getCell(this.cellId); + const result = await this.service.getCell(this.cellId); if (result.ok) { return Ok(this.parser.parserData(result.val.data)); } else { diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/data_persistence.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/data_persistence.ts index 12845aaa64..26e0a4afb9 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/data_persistence.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/data_persistence.ts @@ -27,10 +27,8 @@ export class DateCellDataPersistence extends CellDataPersistence { save(data: CalendarData): Promise> { const payload = DateChangesetPB.fromObject({ cell_path: _makeCellPath(this.cellIdentifier) }); - payload.date = data.date.getUTCMilliseconds.toString(); payload.is_utc = true; - if (data.time !== undefined) { payload.time = data.time; } diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/select_option_bd_svc.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/select_option_bd_svc.ts index fb52dac1fd..1ab1a61148 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/select_option_bd_svc.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/select_option_bd_svc.ts @@ -30,6 +30,7 @@ export class SelectOptionBackendService { export class SelectOptionCellBackendService { constructor(public readonly cellIdentifier: CellIdentifier) {} + // Creates a new option and insert this option to the cell createOption = async (params: { name: string; isSelect?: boolean }) => { const payload = CreateSelectOptionPayloadPB.fromObject({ option_name: params.name, @@ -39,7 +40,7 @@ export class SelectOptionCellBackendService { const result = await DatabaseEventCreateSelectOption(payload); if (result.ok) { - return this._insertOption(result.val, params.isSelect || true); + return await this._insertOption(result.val, params.isSelect || true); } else { return result; } diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/database_controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/database_controller.ts index 25af4535ea..0ef8c27a1f 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/database_controller.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/database_controller.ts @@ -5,7 +5,7 @@ import { DatabasePB } from '../../../../services/backend/models/flowy-database/g import { RowChangedReason, RowInfo } from './row/row_cache'; import { Err, Ok } from 'ts-results'; -export type SubscribeCallback = { +export type SubscribeCallbacks = { onViewChanged?: (data: DatabasePB) => void; onRowsChanged?: (rowInfos: readonly RowInfo[], reason: RowChangedReason) => void; onFieldsChanged?: (fieldInfos: readonly FieldInfo[]) => void; @@ -15,7 +15,7 @@ export class DatabaseController { private backendService: DatabaseBackendService; fieldController: FieldController; databaseViewCache: DatabaseViewCache; - private _callback?: SubscribeCallback; + private _callback?: SubscribeCallbacks; constructor(public readonly viewId: string) { this.backendService = new DatabaseBackendService(viewId); @@ -23,9 +23,9 @@ export class DatabaseController { this.databaseViewCache = new DatabaseViewCache(viewId, this.fieldController); } - subscribe = (callbacks: SubscribeCallback) => { + subscribe = (callbacks: SubscribeCallbacks) => { this._callback = callbacks; - this.fieldController.subscribeOnFieldsChanged(callbacks.onFieldsChanged); + this.fieldController.subscribeOnNumOfFieldsChanged(callbacks.onFieldsChanged); this.databaseViewCache.getRowCache().subscribeOnRowsChanged((reason) => { this._callback?.onRowsChanged?.(this.databaseViewCache.rowInfos, reason); }); diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/field_controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/field_controller.ts index 2d5a3959b2..05ab179325 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/field_controller.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/field_controller.ts @@ -1,49 +1,49 @@ import { Log } from '../../../../utils/log'; import { DatabaseBackendService } from '../database_bd_svc'; import { DatabaseFieldChangesetObserver } from './field_observer'; -import { FieldIdPB, FieldPB, IndexFieldPB } from '../../../../../services/backend/models/flowy-database/field_entities'; +import { FieldIdPB, FieldPB, IndexFieldPB } from '../../../../../services/backend'; import { ChangeNotifier } from '../../../../utils/change_notifier'; export class FieldController { - private fieldListener: DatabaseFieldChangesetObserver; private backendService: DatabaseBackendService; - private fieldNotifier = new FieldNotifier([]); + private numOfFieldsObserver: DatabaseFieldChangesetObserver; + private numOfFieldsNotifier = new NumOfFieldsNotifier([]); constructor(public readonly viewId: string) { this.backendService = new DatabaseBackendService(viewId); - this.fieldListener = new DatabaseFieldChangesetObserver(viewId); + this.numOfFieldsObserver = new DatabaseFieldChangesetObserver(viewId); } dispose = async () => { - this.fieldNotifier.unsubscribe(); - await this.fieldListener.unsubscribe(); + this.numOfFieldsNotifier.unsubscribe(); + await this.numOfFieldsObserver.unsubscribe(); }; get fieldInfos(): readonly FieldInfo[] { - return this.fieldNotifier.fieldInfos; + return this.numOfFieldsNotifier.fieldInfos; } getField = (fieldId: string): FieldInfo | undefined => { - return this.fieldNotifier.fieldInfos.find((element) => element.field.id === fieldId); + return this.numOfFieldsNotifier.fieldInfos.find((element) => element.field.id === fieldId); }; loadFields = async (fieldIds: FieldIdPB[]) => { const result = await this.backendService.getFields(fieldIds); if (result.ok) { - this.fieldNotifier.fieldInfos = result.val.map((field) => new FieldInfo(field)); + this.numOfFieldsNotifier.fieldInfos = result.val.map((field) => new FieldInfo(field)); } else { Log.error(result.val); } }; - subscribeOnFieldsChanged = (callback?: (fieldInfos: readonly FieldInfo[]) => void) => { - return this.fieldNotifier.observer.subscribe((fieldInfos) => { + subscribeOnNumOfFieldsChanged = (callback?: (fieldInfos: readonly FieldInfo[]) => void) => { + return this.numOfFieldsNotifier.observer.subscribe((fieldInfos) => { callback?.(fieldInfos); }); }; listenOnFieldChanges = async () => { - await this.fieldListener.subscribe({ + await this.numOfFieldsObserver.subscribe({ onFieldsChanged: (result) => { if (result.ok) { const changeset = result.val; @@ -57,7 +57,7 @@ export class FieldController { }); }; - _deleteFields = (deletedFields: FieldIdPB[]) => { + private _deleteFields = (deletedFields: FieldIdPB[]) => { if (deletedFields.length === 0) { return; } @@ -68,10 +68,10 @@ export class FieldController { }; const newFieldInfos = [...this.fieldInfos]; newFieldInfos.filter(predicate); - this.fieldNotifier.fieldInfos = newFieldInfos; + this.numOfFieldsNotifier.fieldInfos = newFieldInfos; }; - _insertFields = (insertedFields: IndexFieldPB[]) => { + private _insertFields = (insertedFields: IndexFieldPB[]) => { if (insertedFields.length === 0) { return; } @@ -84,29 +84,28 @@ export class FieldController { newFieldInfos.push(fieldInfo); } }); - this.fieldNotifier.fieldInfos = newFieldInfos; + this.numOfFieldsNotifier.fieldInfos = newFieldInfos; }; - _updateFields = (updatedFields: FieldPB[]) => { + private _updateFields = (updatedFields: FieldPB[]) => { if (updatedFields.length === 0) { return; } const newFieldInfos = [...this.fieldInfos]; updatedFields.forEach((updatedField) => { - newFieldInfos.map((element) => { - if (element.field.id === updatedField.id) { - return updatedField; - } else { - return element; - } + const index = newFieldInfos.findIndex((fieldInfo) => { + return fieldInfo.field.id === updatedField.id; }); + if (index !== -1) { + newFieldInfos.splice(index, 1, new FieldInfo(updatedField)); + } }); - this.fieldNotifier.fieldInfos = newFieldInfos; + this.numOfFieldsNotifier.fieldInfos = newFieldInfos; }; } -class FieldNotifier extends ChangeNotifier { +class NumOfFieldsNotifier extends ChangeNotifier { constructor(private _fieldInfos: FieldInfo[]) { super(); } diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/field_observer.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/field_observer.ts index 423faac3db..9d7daec7df 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/field_observer.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/field_observer.ts @@ -17,7 +17,7 @@ export class DatabaseFieldChangesetObserver { this.notifier?.observer.subscribe(callbacks.onFieldsChanged); this.listener = new DatabaseNotificationObserver({ - viewId: this.viewId, + id: this.viewId, parserHandler: (notification, result) => { switch (notification) { case DatabaseNotification.DidUpdateFields: @@ -50,12 +50,12 @@ export class DatabaseFieldObserver { constructor(public readonly fieldId: string) {} - subscribe = async (callbacks: { onFieldsChanged: FieldNotificationCallback }) => { + subscribe = async (callbacks: { onFieldChanged: FieldNotificationCallback }) => { this._notifier = new ChangeNotifier(); - this._notifier?.observer.subscribe(callbacks.onFieldsChanged); + this._notifier?.observer.subscribe(callbacks.onFieldChanged); this._listener = new DatabaseNotificationObserver({ - viewId: this.fieldId, + id: this.fieldId, parserHandler: (notification, result) => { switch (notification) { case DatabaseNotification.DidUpdateField: diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/type_option/type_option_context.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/type_option/type_option_context.ts index 8c7da56219..3fc184cf20 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/type_option/type_option_context.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/type_option/type_option_context.ts @@ -172,7 +172,7 @@ export class TypeOptionContext { this.fieldObserver = new DatabaseFieldObserver(controller.fieldId); void this.fieldObserver.subscribe({ - onFieldsChanged: () => { + onFieldChanged: () => { void this.getTypeOption(); }, }); diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/type_option/type_option_controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/type_option/type_option_controller.ts index 7993e119b9..1961cae413 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/type_option/type_option_controller.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/type_option/type_option_controller.ts @@ -13,14 +13,20 @@ export class TypeOptionController { private typeOptionBackendSvc: TypeOptionBackendService; // Must call [initialize] if the passed-in fieldInfo is None - constructor(public readonly viewId: string, private initialFieldInfo: Option = None) { + constructor( + public readonly viewId: string, + private readonly initialFieldInfo: Option = None, + private readonly defaultFieldType: FieldType = FieldType.RichText + ) { this.typeOptionData = None; this.typeOptionBackendSvc = new TypeOptionBackendService(viewId); } + // It will create a new field for the defaultFieldType if the [initialFieldInfo] is None. + // Otherwise, it will get the type option of the [initialFieldInfo] initialize = async () => { if (this.initialFieldInfo.none) { - await this.createTypeOption(); + await this.createTypeOption(this.defaultFieldType); } else { await this.getTypeOption(); } @@ -45,8 +51,16 @@ export class TypeOptionController { return new FieldInfo(this.typeOptionData.val.field); }; - switchToField = (fieldType: FieldType) => { - return this.typeOptionBackendSvc.updateTypeOptionType(this.fieldId, fieldType); + switchToField = async (fieldType: FieldType) => { + const result = await this.typeOptionBackendSvc.updateTypeOptionType(this.fieldId, fieldType); + if (result.ok) { + const getResult = await this.typeOptionBackendSvc.getTypeOption(this.fieldId, fieldType); + if (getResult.ok) { + this.updateTypeOptionData(getResult.val); + } + return getResult; + } + return result; }; setFieldName = async (name: string) => { @@ -96,7 +110,7 @@ export class TypeOptionController { }); }; - private createTypeOption = (fieldType: FieldType = FieldType.RichText) => { + private createTypeOption = (fieldType: FieldType) => { return this.typeOptionBackendSvc.createTypeOption(fieldType).then((result) => { if (result.ok) { this.updateTypeOptionData(result.val); diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/notifications/observer.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/notifications/observer.ts index 186e3276b0..aca10af257 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/notifications/observer.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/notifications/observer.ts @@ -6,10 +6,10 @@ import { Result } from 'ts-results'; export type ParserHandler = (notification: DatabaseNotification, result: Result) => void; export class DatabaseNotificationObserver extends AFNotificationObserver { - constructor(params: { viewId?: string; parserHandler: ParserHandler }) { + constructor(params: { id?: string; parserHandler: ParserHandler }) { const parser = new DatabaseNotificationParser({ callback: params.parserHandler, - id: params.viewId, + id: params.id, }); super(parser); } diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/row/row_cache.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/row/row_cache.ts index a9d7499f19..2d68d161a4 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/row/row_cache.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/row/row_cache.ts @@ -19,26 +19,26 @@ import { Log } from '../../../../utils/log'; export type CellByFieldId = Map; export class RowCache { - private readonly _rowList: RowList; - private readonly _cellCache: CellCache; - private readonly _notifier: RowChangeNotifier; + private readonly rowList: RowList; + private readonly cellCache: CellCache; + private readonly notifier: RowChangeNotifier; constructor(public readonly viewId: string, private readonly getFieldInfos: () => readonly FieldInfo[]) { - this._rowList = new RowList(); - this._cellCache = new CellCache(viewId); - this._notifier = new RowChangeNotifier(); + this.rowList = new RowList(); + this.cellCache = new CellCache(viewId); + this.notifier = new RowChangeNotifier(); } get rows(): readonly RowInfo[] { - return this._rowList.rows; + return this.rowList.rows; } getCellCache = () => { - return this._cellCache; + return this.cellCache; }; loadCells = async (rowId: string): Promise => { - const opRow = this._rowList.getRow(rowId); + const opRow = this.rowList.getRow(rowId); if (opRow.some) { return this._toCellMap(opRow.val.row.id, this.getFieldInfos()); } else { @@ -54,7 +54,7 @@ export class RowCache { }; subscribeOnRowsChanged = (callback: (reason: RowChangedReason, cellMap?: Map) => void) => { - return this._notifier.observer.subscribe((change) => { + return this.notifier.observer.subscribe((change) => { if (change.rowId !== undefined) { callback(change.reason, this._toCellMap(change.rowId, this.getFieldInfos())); } else { @@ -65,18 +65,19 @@ export class RowCache { onFieldUpdated = (fieldInfo: FieldInfo) => { // Remove the cell data if the corresponding field was changed - this._cellCache.removeWithFieldId(fieldInfo.field.id); + this.cellCache.removeWithFieldId(fieldInfo.field.id); }; - onNumberOfFieldsUpdated = () => { - this._notifier.withChange(RowChangedReason.FieldDidChanged); + onNumberOfFieldsUpdated = (fieldInfos: readonly FieldInfo[]) => { + this.rowList.setFieldInfos(fieldInfos); + this.notifier.withChange(RowChangedReason.FieldDidChanged); }; initializeRows = (rows: RowPB[]) => { rows.forEach((rowPB) => { - this._rowList.push(this._toRowInfo(rowPB)); + this.rowList.push(this._toRowInfo(rowPB)); }); - this._notifier.withChange(RowChangedReason.ReorderRows); + this.notifier.withChange(RowChangedReason.ReorderRows); }; applyRowsChanged = (changeset: RowsChangesetPB) => { @@ -91,15 +92,15 @@ export class RowCache { }; applyReorderRows = (rowIds: string[]) => { - this._rowList.reorderByRowIds(rowIds); - this._notifier.withChange(RowChangedReason.ReorderRows); + this.rowList.reorderByRowIds(rowIds); + this.notifier.withChange(RowChangedReason.ReorderRows); }; applyReorderSingleRow = (reorderRow: ReorderSingleRowPB) => { - const rowInfo = this._rowList.getRow(reorderRow.row_id); + const rowInfo = this.rowList.getRow(reorderRow.row_id); if (rowInfo !== undefined) { - this._rowList.move({ rowId: reorderRow.row_id, fromIndex: reorderRow.old_index, toIndex: reorderRow.new_index }); - this._notifier.withChange(RowChangedReason.ReorderSingleRow, reorderRow.row_id); + this.rowList.move({ rowId: reorderRow.row_id, fromIndex: reorderRow.old_index, toIndex: reorderRow.new_index }); + this.notifier.withChange(RowChangedReason.ReorderSingleRow, reorderRow.row_id); } }; @@ -108,14 +109,14 @@ export class RowCache { return; } const updatedRow = opRow.row; - const option = this._rowList.getRowWithIndex(updatedRow.id); + const option = this.rowList.getRowWithIndex(updatedRow.id); if (option.some) { const { rowInfo, index } = option.val; - this._rowList.remove(rowInfo.row.id); - this._rowList.insert(index, rowInfo.copyWith({ row: updatedRow })); + this.rowList.remove(rowInfo.row.id); + this.rowList.insert(index, rowInfo.copyWith({ row: updatedRow })); } else { const newRowInfo = new RowInfo(this.viewId, this.getFieldInfos(), updatedRow); - this._rowList.push(newRowInfo); + this.rowList.push(newRowInfo); } }; @@ -126,9 +127,9 @@ export class RowCache { private _deleteRows = (rowIds: string[]) => { rowIds.forEach((rowId) => { - const deletedRow = this._rowList.remove(rowId); + const deletedRow = this.rowList.remove(rowId); if (deletedRow !== undefined) { - this._notifier.withChange(RowChangedReason.Delete, deletedRow.rowInfo.row.id); + this.notifier.withChange(RowChangedReason.Delete, deletedRow.rowInfo.row.id); } }); }; @@ -136,9 +137,9 @@ export class RowCache { private _insertRows = (rows: InsertedRowPB[]) => { rows.forEach((insertedRow) => { const rowInfo = this._toRowInfo(insertedRow.row); - const insertedIndex = this._rowList.insert(insertedRow.index, rowInfo); + const insertedIndex = this.rowList.insert(insertedRow.index, rowInfo); if (insertedIndex !== undefined) { - this._notifier.withChange(RowChangedReason.Insert, insertedIndex.rowId); + this.notifier.withChange(RowChangedReason.Insert, insertedIndex.rowId); } }); }; @@ -152,39 +153,39 @@ export class RowCache { updatedRows.forEach((updatedRow) => { updatedRow.field_ids.forEach((fieldId) => { const key = new CellCacheKey(fieldId, updatedRow.row.id); - this._cellCache.remove(key); + this.cellCache.remove(key); }); rowInfos.push(this._toRowInfo(updatedRow.row)); }); - const updatedIndexs = this._rowList.insertRows(rowInfos); + const updatedIndexs = this.rowList.insertRows(rowInfos); updatedIndexs.forEach((row) => { - this._notifier.withChange(RowChangedReason.Update, row.rowId); + this.notifier.withChange(RowChangedReason.Update, row.rowId); }); }; private _hideRows = (rowIds: string[]) => { rowIds.forEach((rowId) => { - const deletedRow = this._rowList.remove(rowId); + const deletedRow = this.rowList.remove(rowId); if (deletedRow !== undefined) { - this._notifier.withChange(RowChangedReason.Delete, deletedRow.rowInfo.row.id); + this.notifier.withChange(RowChangedReason.Delete, deletedRow.rowInfo.row.id); } }); }; private _displayRows = (insertedRows: InsertedRowPB[]) => { insertedRows.forEach((insertedRow) => { - const insertedIndex = this._rowList.insert(insertedRow.index, this._toRowInfo(insertedRow.row)); + const insertedIndex = this.rowList.insert(insertedRow.index, this._toRowInfo(insertedRow.row)); if (insertedIndex !== undefined) { - this._notifier.withChange(RowChangedReason.Insert, insertedIndex.rowId); + this.notifier.withChange(RowChangedReason.Insert, insertedIndex.rowId); } }); }; dispose = async () => { - this._notifier.dispose(); + this.notifier.dispose(); }; private _toRowInfo = (rowPB: RowPB) => { @@ -219,7 +220,6 @@ class RowList { return Some(rowInfo); } }; - getRowWithIndex = (rowId: string): Option<{ rowInfo: RowInfo; index: number }> => { const rowInfo = this._rowInfoByRowId.get(rowId); if (rowInfo !== undefined) { @@ -322,6 +322,14 @@ class RowList { includes = (rowId: string): boolean => { return this._rowInfoByRowId.has(rowId); }; + + setFieldInfos = (fieldInfos: readonly FieldInfo[]) => { + const newRowInfos: RowInfo[] = []; + this._rowInfos.forEach((rowInfo) => { + newRowInfos.push(rowInfo.copyWith({ fieldInfos: fieldInfos })); + }); + this._rowInfos = newRowInfos; + }; } export class RowInfo { @@ -331,8 +339,8 @@ export class RowInfo { public readonly row: RowPB ) {} - copyWith = (params: { row?: RowPB }) => { - return new RowInfo(this.viewId, this.fieldInfos, params.row || this.row); + copyWith = (params: { row?: RowPB; fieldInfos?: readonly FieldInfo[] }) => { + return new RowInfo(this.viewId, params.fieldInfos || this.fieldInfos, params.row || this.row); }; } diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/view/database_view_cache.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/view/database_view_cache.ts index 29b783864a..2a5d9303e6 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/view/database_view_cache.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/view/database_view_cache.ts @@ -12,10 +12,11 @@ export class DatabaseViewCache { constructor(public readonly viewId: string, fieldController: FieldController) { this.rowsObserver = new DatabaseViewRowsObserver(viewId); this.rowCache = new RowCache(viewId, () => fieldController.fieldInfos); - this.fieldSubscription = fieldController.subscribeOnFieldsChanged((fieldInfos) => { + this.fieldSubscription = fieldController.subscribeOnNumOfFieldsChanged((fieldInfos) => { fieldInfos.forEach((fieldInfo) => { this.rowCache.onFieldUpdated(fieldInfo); }); + this.rowCache.onNumberOfFieldsUpdated(fieldInfos); }); } diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/view/view_row_observer.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/view/view_row_observer.ts index 1bc3f4de6e..198c86e598 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/view/view_row_observer.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/view/view_row_observer.ts @@ -38,7 +38,7 @@ export class DatabaseViewRowsObserver { this.reorderSingleRowNotifier.observer.subscribe(callbacks.onReorderSingleRow); this._listener = new DatabaseNotificationObserver({ - viewId: this.viewId, + id: this.viewId, parserHandler: (notification, result) => { switch (notification) { case DatabaseNotification.DidUpdateViewRowsVisibility: diff --git a/frontend/rust-lib/flowy-codegen/src/ts_event/event_template.tera b/frontend/rust-lib/flowy-codegen/src/ts_event/event_template.tera index 8444f304a8..2b23f78768 100644 --- a/frontend/rust-lib/flowy-codegen/src/ts_event/event_template.tera +++ b/frontend/rust-lib/flowy-codegen/src/ts_event/event_template.tera @@ -27,6 +27,7 @@ export async function {{ event_func_name }}(): Promise Result; @@ -118,7 +118,6 @@ impl DatabaseManager { Ok(()) } - #[tracing::instrument(level = "debug", skip_all, err)] pub async fn create_database_block>( &self, block_id: T, @@ -141,33 +140,32 @@ impl DatabaseManager { .await } + #[tracing::instrument(level = "debug", skip_all)] pub async fn close_database_view>(&self, view_id: T) -> FlowyResult<()> { let view_id = view_id.as_ref(); let database_info = self.database_ref_indexer.get_database_with_view(view_id)?; tracing::Span::current().record("database_id", &database_info.database_id); - let mut should_remove_editor = false; - if let Some(database_editor) = self - .editors_by_database_id - .write() - .await - .get(&database_info.database_id) - { - database_editor.close_view_editor(view_id).await; - should_remove_editor = database_editor.number_of_ref_views().await == 0; - if should_remove_editor { - database_editor.dispose().await; - } + match self.editors_by_database_id.try_write() { + Ok(mut write_guard) => { + if let Some(database_editor) = write_guard.remove(&database_info.database_id) { + database_editor.close_view_editor(view_id).await; + if database_editor.number_of_ref_views().await == 0 { + database_editor.dispose().await; + } else { + self + .editors_by_database_id + .write() + .await + .insert(database_info.database_id, database_editor); + } + } + }, + Err(_) => { + tracing::error!("Try to get the lock of editors_by_database_id failed"); + }, } - if should_remove_editor { - tracing::debug!("Close database base editor: {}", database_info.database_id); - self - .editors_by_database_id - .write() - .await - .remove(&database_info.database_id); - } Ok(()) } @@ -235,12 +233,10 @@ impl DatabaseManager { pool: Arc, ) -> Result, FlowyError> { let user = self.database_user.clone(); - tracing::debug!("Open database view: {}", view_id); let (base_view_pad, base_view_rev_manager) = make_database_view_revision_pad(view_id, user.clone()).await?; let mut database_id = base_view_pad.database_id.clone(); - - tracing::debug!("Open database: {}", database_id); + tracing::debug!("Open database: {} with view: {}", database_id, view_id); if database_id.is_empty() { // Before the database_id concept comes up, we used the view_id directly. So if // the database_id is empty, which means we can used the view_id. After the version 0.1.1, diff --git a/frontend/rust-lib/flowy-database/src/services/database/database_editor.rs b/frontend/rust-lib/flowy-database/src/services/database/database_editor.rs index f497051afb..701ff72a98 100644 --- a/frontend/rust-lib/flowy-database/src/services/database/database_editor.rs +++ b/frontend/rust-lib/flowy-database/src/services/database/database_editor.rs @@ -116,13 +116,13 @@ impl DatabaseEditor { self.database_views.open(view_editor).await } - #[tracing::instrument(name = "Close database editor view", level = "debug", skip_all)] + #[tracing::instrument(level = "debug", skip_all)] pub async fn close_view_editor(&self, view_id: &str) { - self.rev_manager.generate_snapshot().await; self.database_views.close(view_id).await; } pub async fn dispose(&self) { + self.rev_manager.generate_snapshot().await; self.database_blocks.close().await; self.rev_manager.close().await; } diff --git a/frontend/rust-lib/flowy-database/src/services/database_view/editor.rs b/frontend/rust-lib/flowy-database/src/services/database_view/editor.rs index 3454290ce4..e6918b9f50 100644 --- a/frontend/rust-lib/flowy-database/src/services/database_view/editor.rs +++ b/frontend/rust-lib/flowy-database/src/services/database_view/editor.rs @@ -178,12 +178,12 @@ impl DatabaseViewEditor { .await } - #[tracing::instrument(name = "close grid view editor", level = "trace", skip_all)] + #[tracing::instrument(name = "close database view editor", level = "trace", skip_all)] pub async fn close(&self) { self.rev_manager.generate_snapshot().await; self.rev_manager.close().await; self.filter_controller.close().await; - self.sort_controller.read().await.close().await; + self.sort_controller.write().await.close().await; } pub async fn handle_block_event(&self, event: Cow<'_, DatabaseBlockEvent>) { @@ -509,8 +509,8 @@ impl DatabaseViewEditor { .did_receive_changes(SortChangeset::from_insert(sort_type)) .await }; - self.notify_did_update_sort(changeset).await; drop(sort_controller); + self.notify_did_update_sort(changeset).await; Ok(sort_rev) } @@ -539,7 +539,7 @@ impl DatabaseViewEditor { pub async fn v_delete_all_sorts(&self) -> FlowyResult<()> { let all_sorts = self.v_get_all_sorts().await; - self.sort_controller.write().await.delete_all_sorts().await; + // self.sort_controller.write().await.delete_all_sorts().await; self .modify(|pad| { let changeset = pad.delete_all_sorts()?; diff --git a/frontend/rust-lib/flowy-database/src/services/database_view/editor_manager.rs b/frontend/rust-lib/flowy-database/src/services/database_view/editor_manager.rs index 854d94f676..05975b1fb2 100644 --- a/frontend/rust-lib/flowy-database/src/services/database_view/editor_manager.rs +++ b/frontend/rust-lib/flowy-database/src/services/database_view/editor_manager.rs @@ -60,7 +60,11 @@ impl DatabaseViews { } pub async fn close(&self, view_id: &str) { - self.view_editors.write().await.remove(view_id).await; + if let Ok(mut view_editors) = self.view_editors.try_write() { + view_editors.remove(view_id).await; + } else { + tracing::error!("Try to get the lock of view_editors failed"); + } } pub async fn number_of_views(&self) -> usize { diff --git a/frontend/rust-lib/flowy-database/src/services/field/type_options/type_option_cell.rs b/frontend/rust-lib/flowy-database/src/services/field/type_options/type_option_cell.rs index 36e5f99466..b3f1e8e8cd 100644 --- a/frontend/rust-lib/flowy-database/src/services/field/type_options/type_option_cell.rs +++ b/frontend/rust-lib/flowy-database/src/services/field/type_options/type_option_cell.rs @@ -289,6 +289,7 @@ where decoded_field_type: &FieldType, field_rev: &FieldRevision, ) -> FlowyResult { + // tracing::debug!("get_cell_data: {:?}", std::any::type_name::()); let cell_data = if self.transformable() { match self.transform_type_option_cell_str(&cell_str, decoded_field_type, field_rev) { None => self.get_decoded_cell_data(cell_str, decoded_field_type, field_rev)?, diff --git a/frontend/rust-lib/flowy-database/src/services/persistence/rev_sqlite/block_impl.rs b/frontend/rust-lib/flowy-database/src/services/persistence/rev_sqlite/block_impl.rs index bfd640bba1..fadaec2ff9 100644 --- a/frontend/rust-lib/flowy-database/src/services/persistence/rev_sqlite/block_impl.rs +++ b/frontend/rust-lib/flowy-database/src/services/persistence/rev_sqlite/block_impl.rs @@ -22,7 +22,7 @@ impl RevisionDiskCache> for SQLiteDatabaseBlockRevisionPersi fn create_revision_records(&self, revision_records: Vec) -> Result<(), Self::Error> { let conn = self.pool.get().map_err(internal_error)?; - GridMetaRevisionSql::create(revision_records, &conn)?; + DatabaseBlockMetaRevisionSql::create(revision_records, &conn)?; Ok(()) } @@ -36,7 +36,7 @@ impl RevisionDiskCache> for SQLiteDatabaseBlockRevisionPersi rev_ids: Option>, ) -> Result, Self::Error> { let conn = self.pool.get().map_err(internal_error)?; - let records = GridMetaRevisionSql::read(&self.user_id, object_id, rev_ids, &conn)?; + let records = DatabaseBlockMetaRevisionSql::read(&self.user_id, object_id, rev_ids, &conn)?; Ok(records) } @@ -47,7 +47,7 @@ impl RevisionDiskCache> for SQLiteDatabaseBlockRevisionPersi ) -> Result, Self::Error> { let conn = &*self.pool.get().map_err(internal_error)?; let revisions = - GridMetaRevisionSql::read_with_range(&self.user_id, object_id, range.clone(), conn)?; + DatabaseBlockMetaRevisionSql::read_with_range(&self.user_id, object_id, range.clone(), conn)?; Ok(revisions) } @@ -55,7 +55,7 @@ impl RevisionDiskCache> for SQLiteDatabaseBlockRevisionPersi let conn = &*self.pool.get().map_err(internal_error)?; conn.immediate_transaction::<_, FlowyError, _>(|| { for changeset in changesets { - GridMetaRevisionSql::update(changeset, conn)?; + DatabaseBlockMetaRevisionSql::update(changeset, conn)?; } Ok(()) })?; @@ -68,7 +68,7 @@ impl RevisionDiskCache> for SQLiteDatabaseBlockRevisionPersi rev_ids: Option>, ) -> Result<(), Self::Error> { let conn = &*self.pool.get().map_err(internal_error)?; - GridMetaRevisionSql::delete(object_id, rev_ids, conn)?; + DatabaseBlockMetaRevisionSql::delete(object_id, rev_ids, conn)?; Ok(()) } @@ -80,8 +80,8 @@ impl RevisionDiskCache> for SQLiteDatabaseBlockRevisionPersi ) -> Result<(), Self::Error> { let conn = self.pool.get().map_err(internal_error)?; conn.immediate_transaction::<_, FlowyError, _>(|| { - GridMetaRevisionSql::delete(object_id, deleted_rev_ids, &conn)?; - GridMetaRevisionSql::create(inserted_records, &conn)?; + DatabaseBlockMetaRevisionSql::delete(object_id, deleted_rev_ids, &conn)?; + DatabaseBlockMetaRevisionSql::create(inserted_records, &conn)?; Ok(()) }) } @@ -96,8 +96,8 @@ impl SQLiteDatabaseBlockRevisionPersistence { } } -struct GridMetaRevisionSql(); -impl GridMetaRevisionSql { +struct DatabaseBlockMetaRevisionSql(); +impl DatabaseBlockMetaRevisionSql { fn create(revision_records: Vec, conn: &SqliteConnection) -> Result<(), FlowyError> { // Batch insert: https://diesel.rs/guides/all-about-inserts.html @@ -105,7 +105,8 @@ impl GridMetaRevisionSql { .into_iter() .map(|record| { tracing::trace!( - "[GridMetaRevisionSql] create revision: {}:{:?}", + "[{}] create revision: {}:{:?}", + std::any::type_name::(), record.revision.object_id, record.revision.rev_id ); @@ -133,7 +134,8 @@ impl GridMetaRevisionSql { .filter(dsl::object_id.eq(changeset.object_id)); let _ = update(filter).set(dsl::state.eq(state)).execute(conn)?; tracing::debug!( - "[GridMetaRevisionSql] update revision:{} state:to {:?}", + "[{}] update revision:{} state:to {:?}", + std::any::type_name::(), changeset.rev_id, changeset.state ); @@ -193,7 +195,8 @@ impl GridMetaRevisionSql { if let Some(rev_ids) = rev_ids { tracing::trace!( - "[GridMetaRevisionSql] Delete revision: {}:{:?}", + "[{}] Delete revision: {}:{:?}", + std::any::type_name::(), object_id, rev_ids ); @@ -201,7 +204,11 @@ impl GridMetaRevisionSql { } let affected_row = sql.execute(conn)?; - tracing::trace!("[GridMetaRevisionSql] Delete {} rows", affected_row); + tracing::trace!( + "[{}] Delete {} rows", + std::any::type_name::(), + affected_row + ); Ok(()) } } diff --git a/frontend/rust-lib/flowy-database/src/services/persistence/rev_sqlite/database_impl.rs b/frontend/rust-lib/flowy-database/src/services/persistence/rev_sqlite/database_impl.rs index d146a01116..7a84e02f32 100644 --- a/frontend/rust-lib/flowy-database/src/services/persistence/rev_sqlite/database_impl.rs +++ b/frontend/rust-lib/flowy-database/src/services/persistence/rev_sqlite/database_impl.rs @@ -104,7 +104,8 @@ impl DatabaseRevisionSql { .into_iter() .map(|record| { tracing::trace!( - "[GridRevisionSql] create revision: {}:{:?}", + "[{}] create revision: {}:{:?}", + std::any::type_name::(), record.revision.object_id, record.revision.rev_id ); @@ -132,7 +133,8 @@ impl DatabaseRevisionSql { .filter(dsl::object_id.eq(changeset.object_id)); let _ = update(filter).set(dsl::state.eq(state)).execute(conn)?; tracing::debug!( - "[GridRevisionSql] update revision:{} state:to {:?}", + "[{}] update revision:{} state:to {:?}", + std::any::type_name::(), changeset.rev_id, changeset.state ); @@ -192,7 +194,8 @@ impl DatabaseRevisionSql { if let Some(rev_ids) = rev_ids { tracing::trace!( - "[GridRevisionSql] Delete revision: {}:{:?}", + "[{}] Delete revision: {}:{:?}", + std::any::type_name::(), object_id, rev_ids ); @@ -200,7 +203,11 @@ impl DatabaseRevisionSql { } let affected_row = sql.execute(conn)?; - tracing::trace!("[GridRevisionSql] Delete {} rows", affected_row); + tracing::trace!( + "[{}] Delete {} rows", + std::any::type_name::(), + affected_row + ); Ok(()) } } diff --git a/frontend/rust-lib/flowy-database/src/services/persistence/rev_sqlite/view_impl.rs b/frontend/rust-lib/flowy-database/src/services/persistence/rev_sqlite/view_impl.rs index df076b62e7..d6f4340f7e 100644 --- a/frontend/rust-lib/flowy-database/src/services/persistence/rev_sqlite/view_impl.rs +++ b/frontend/rust-lib/flowy-database/src/services/persistence/rev_sqlite/view_impl.rs @@ -31,7 +31,7 @@ impl RevisionDiskCache> for SQLiteDatabaseViewRevisionPersis fn create_revision_records(&self, revision_records: Vec) -> Result<(), Self::Error> { let conn = self.pool.get().map_err(internal_error)?; - GridViewRevisionSql::create(revision_records, &conn)?; + DatabaseViewRevisionSql::create(revision_records, &conn)?; Ok(()) } @@ -45,7 +45,7 @@ impl RevisionDiskCache> for SQLiteDatabaseViewRevisionPersis rev_ids: Option>, ) -> Result, Self::Error> { let conn = self.pool.get().map_err(internal_error)?; - let records = GridViewRevisionSql::read(&self.user_id, object_id, rev_ids, &conn)?; + let records = DatabaseViewRevisionSql::read(&self.user_id, object_id, rev_ids, &conn)?; Ok(records) } @@ -56,7 +56,7 @@ impl RevisionDiskCache> for SQLiteDatabaseViewRevisionPersis ) -> Result, Self::Error> { let conn = &*self.pool.get().map_err(internal_error)?; let revisions = - GridViewRevisionSql::read_with_range(&self.user_id, object_id, range.clone(), conn)?; + DatabaseViewRevisionSql::read_with_range(&self.user_id, object_id, range.clone(), conn)?; Ok(revisions) } @@ -64,7 +64,7 @@ impl RevisionDiskCache> for SQLiteDatabaseViewRevisionPersis let conn = &*self.pool.get().map_err(internal_error)?; conn.immediate_transaction::<_, FlowyError, _>(|| { for changeset in changesets { - GridViewRevisionSql::update(changeset, conn)?; + DatabaseViewRevisionSql::update(changeset, conn)?; } Ok(()) })?; @@ -77,7 +77,7 @@ impl RevisionDiskCache> for SQLiteDatabaseViewRevisionPersis rev_ids: Option>, ) -> Result<(), Self::Error> { let conn = &*self.pool.get().map_err(internal_error)?; - GridViewRevisionSql::delete(object_id, rev_ids, conn)?; + DatabaseViewRevisionSql::delete(object_id, rev_ids, conn)?; Ok(()) } @@ -89,22 +89,23 @@ impl RevisionDiskCache> for SQLiteDatabaseViewRevisionPersis ) -> Result<(), Self::Error> { let conn = self.pool.get().map_err(internal_error)?; conn.immediate_transaction::<_, FlowyError, _>(|| { - GridViewRevisionSql::delete(object_id, deleted_rev_ids, &conn)?; - GridViewRevisionSql::create(inserted_records, &conn)?; + DatabaseViewRevisionSql::delete(object_id, deleted_rev_ids, &conn)?; + DatabaseViewRevisionSql::create(inserted_records, &conn)?; Ok(()) }) } } -struct GridViewRevisionSql(); -impl GridViewRevisionSql { +struct DatabaseViewRevisionSql(); +impl DatabaseViewRevisionSql { fn create(revision_records: Vec, conn: &SqliteConnection) -> Result<(), FlowyError> { // Batch insert: https://diesel.rs/guides/all-about-inserts.html let records = revision_records .into_iter() .map(|record| { tracing::trace!( - "[GridViewRevisionSql] create revision: {}:{:?}", + "[{}] create revision: {}:{:?}", + std::any::type_name::(), record.revision.object_id, record.revision.rev_id ); @@ -132,7 +133,8 @@ impl GridViewRevisionSql { .filter(dsl::object_id.eq(changeset.object_id)); let _ = update(filter).set(dsl::state.eq(state)).execute(conn)?; tracing::debug!( - "[GridViewRevisionSql] update revision:{} state:to {:?}", + "[{}] update revision:{} state:to {:?}", + std::any::type_name::(), changeset.rev_id, changeset.state ); @@ -192,7 +194,8 @@ impl GridViewRevisionSql { if let Some(rev_ids) = rev_ids { tracing::trace!( - "[GridViewRevisionSql] Delete revision: {}:{:?}", + "[{}] Delete revision: {}:{:?}", + std::any::type_name::(), object_id, rev_ids ); @@ -200,7 +203,11 @@ impl GridViewRevisionSql { } let affected_row = sql.execute(conn)?; - tracing::trace!("[GridViewRevisionSql] Delete {} rows", affected_row); + tracing::trace!( + "[{}] Delete {} rows", + std::any::type_name::(), + affected_row + ); Ok(()) } } diff --git a/frontend/rust-lib/flowy-database/src/services/sort/controller.rs b/frontend/rust-lib/flowy-database/src/services/sort/controller.rs index 49e048754a..ab6ac9b721 100644 --- a/frontend/rust-lib/flowy-database/src/services/sort/controller.rs +++ b/frontend/rust-lib/flowy-database/src/services/sort/controller.rs @@ -63,12 +63,11 @@ impl SortController { } pub async fn close(&self) { - self - .task_scheduler - .write() - .await - .unregister_handler(&self.handler_id) - .await; + if let Ok(mut task_scheduler) = self.task_scheduler.try_write() { + // task_scheduler.unregister_handler(&self.handler_id).await; + } else { + tracing::error!("Try to get the lock of task_scheduler failed"); + } } pub async fn did_receive_row_changed(&self, row_id: &str) { diff --git a/frontend/rust-lib/flowy-folder/src/services/persistence/rev_sqlite/folder_rev_sqlite.rs b/frontend/rust-lib/flowy-folder/src/services/persistence/rev_sqlite/folder_rev_sqlite.rs index 59df4f4c2a..de32507c4e 100644 --- a/frontend/rust-lib/flowy-folder/src/services/persistence/rev_sqlite/folder_rev_sqlite.rs +++ b/frontend/rust-lib/flowy-folder/src/services/persistence/rev_sqlite/folder_rev_sqlite.rs @@ -106,7 +106,8 @@ impl FolderRevisionSql { .into_iter() .map(|record| { tracing::trace!( - "[TextRevisionSql] create revision: {}:{:?}", + "[{}] create revision: {}:{:?}", + std::any::type_name::(), record.revision.object_id, record.revision.rev_id ); @@ -135,7 +136,8 @@ impl FolderRevisionSql { .filter(dsl::doc_id.eq(changeset.object_id)); let _ = update(filter).set(dsl::state.eq(state)).execute(conn)?; tracing::debug!( - "[TextRevisionSql] update revision:{} state:to {:?}", + "[{}] update revision:{} state:to {:?}", + std::any::type_name::(), changeset.rev_id, changeset.state ); @@ -193,7 +195,8 @@ impl FolderRevisionSql { if let Some(rev_ids) = rev_ids { tracing::trace!( - "[TextRevisionSql] Delete revision: {}:{:?}", + "[{}] Delete revision: {}:{:?}", + std::any::type_name::(), object_id, rev_ids ); @@ -201,7 +204,11 @@ impl FolderRevisionSql { } let affected_row = sql.execute(conn)?; - tracing::trace!("[TextRevisionSql] Delete {} rows", affected_row); + tracing::trace!( + "[{}] Delete {} rows", + std::any::type_name::(), + affected_row + ); Ok(()) } } diff --git a/frontend/rust-lib/flowy-task/src/scheduler.rs b/frontend/rust-lib/flowy-task/src/scheduler.rs index ee9e4d979a..ae61bb08bb 100644 --- a/frontend/rust-lib/flowy-task/src/scheduler.rs +++ b/frontend/rust-lib/flowy-task/src/scheduler.rs @@ -5,6 +5,7 @@ use anyhow::Error; use lib_infra::async_trait::async_trait; use lib_infra::future::BoxResultFuture; use lib_infra::ref_map::{RefCountHashMap, RefCountValue}; +use std::collections::HashMap; use std::sync::Arc; use std::time::Duration; @@ -15,7 +16,7 @@ pub struct TaskDispatcher { queue: TaskQueue, store: TaskStore, timeout: Duration, - handlers: RefCountHashMap, + handlers: HashMap>, notifier: watch::Sender, pub(crate) notifier_rx: Option>, @@ -28,7 +29,7 @@ impl TaskDispatcher { queue: TaskQueue::new(), store: TaskStore::new(), timeout, - handlers: RefCountHashMap::new(), + handlers: HashMap::new(), notifier, notifier_rx: Some(notifier_rx), } @@ -39,13 +40,17 @@ impl TaskDispatcher { T: TaskHandler, { let handler_id = handler.handler_id().to_owned(); - self - .handlers - .insert(handler_id, RefCountTaskHandler(Arc::new(handler))); + self.handlers.insert(handler_id, Arc::new(handler)); } pub async fn unregister_handler>(&mut self, handler_id: T) { - self.handlers.remove(handler_id.as_ref()).await; + if let Some(handler) = self.handlers.remove(handler_id.as_ref()) { + tracing::trace!( + "{}:{} is unregistered", + handler.handler_name(), + handler.handler_id() + ); + } } pub fn stop(&mut self) { @@ -54,6 +59,7 @@ impl TaskDispatcher { self.store.clear(); } + #[tracing::instrument(level = "trace", skip_all)] pub(crate) async fn process_next_task(&mut self) -> Option<()> { let pending_task = self.queue.mut_head(|list| list.pop())?; let mut task = self.store.remove_task(&pending_task.id)?; @@ -69,25 +75,25 @@ impl TaskDispatcher { let content = task.content.take()?; if let Some(handler) = self.handlers.get(&task.handler_id) { task.set_state(TaskState::Processing); - tracing::trace!( - "Run {} task with content: {:?}", - handler.handler_name(), - content - ); + tracing::trace!("{} task is running", handler.handler_name(),); match tokio::time::timeout(self.timeout, handler.run(content)).await { Ok(result) => match result { - Ok(_) => task.set_state(TaskState::Done), + Ok(_) => { + tracing::trace!("{} task is done", handler.handler_name(),); + task.set_state(TaskState::Done) + }, Err(e) => { - tracing::error!("Process {} task failed: {:?}", handler.handler_name(), e); + tracing::error!("{} task is failed: {:?}", handler.handler_name(), e); task.set_state(TaskState::Failure); }, }, Err(e) => { - tracing::error!("Process {} task timeout: {:?}", handler.handler_name(), e); + tracing::error!("{} task is timeout: {:?}", handler.handler_name(), e); task.set_state(TaskState::Timeout); }, } } else { + tracing::trace!("{} is cancel", task.handler_id); task.set_state(TaskState::Cancel); } let _ = ret.send(task.into()); @@ -197,18 +203,3 @@ where (**self).run(content) } } -#[derive(Clone)] -struct RefCountTaskHandler(Arc); - -#[async_trait] -impl RefCountValue for RefCountTaskHandler { - async fn did_remove(&self) {} -} - -impl std::ops::Deref for RefCountTaskHandler { - type Target = Arc; - - fn deref(&self) -> &Self::Target { - &self.0 - } -}