Improve batched Column performance - #2260
Conversation
e33f8c3 to
68ba14a
Compare
| } | ||
|
|
||
| protected _serializeKey(key: TKey): string { | ||
| return dataObjects.stringify(key); |
There was a problem hiding this comment.
Maybe deserialize the key first. Consider a SmartColumn<SomeDo> using the RestLookupCall to lookup its values. Depending on whether SomeDo is a BaseDoEntity and/or how the data of the table is loaded the value of the cell might be a BaseDoEntity. But as the resulting key of the RestLookupCall will always be a pojo simply using dataObjects.stringify will result in different values if the pojo has e.g. a _typeVersion set.
d96b915 to
ab8eef4
Compare
ab8eef4 to
05fbb7a
Compare
| } | ||
|
|
||
| protected _batchFormatValues(keys: TValue[][]): JQuery.Promise<Map<TValue[], string>> { | ||
| const allKeys = [...new Set(keys.flat())]; |
There was a problem hiding this comment.
Does this work for complex objects?
There was a problem hiding this comment.
Should be ok, because key-identity is handled by BatchCall. Correct?
| this._tableColumnsChangedHandler = this._onTableColumnsChanged.bind(this); | ||
| this._realWidth = null; | ||
|
|
||
| this._batchFormat = null; |
There was a problem hiding this comment.
Initialization to null not necessary for this internal property (or can be merged with the property declaration).
| // If every cell of a row is empty the row would collapse, using nbsp makes sure the row is as height as the others even if it is empty | ||
| content = ' '; | ||
| cssClass = strings.join(' ', cssClass, 'empty'); | ||
| } else if (cell.flowsLeft) { |
There was a problem hiding this comment.
Event shorter:
} else {
content = cell.flowsLeft ? text + icon : icon + text;
}
| }); | ||
|
|
||
| setCellTextDeferred(promise: JQuery.Promise<BatchCallResult<TValue, unknown>>) { | ||
| // (then) promises always resolve asynchronously which means the text will always be set later after row is initialized and will generate an update row event. |
There was a problem hiding this comment.
What does (then) at the start of this sentence mean?
| this._batchFormat.addKey(value); | ||
| return this._batchFormat.promise(); | ||
| } | ||
| return scout.nvl(value, ''); |
There was a problem hiding this comment.
If value is not a string, the result will also not be a string, which contradicts the declared return type of this method. Maybe just convert it to String, same as in _batchFormatResultToCellText()?
| return; | ||
| } | ||
|
|
||
| promise |
There was a problem hiding this comment.
Since we are not returning the result, it should be safe to use then/catch instead of done/fail.
|
|
||
| protected _batchFormatValues(keys: TValue[]): JQuery.Promise<Map<TValue, string>> { | ||
| const lookupCall = this.lookupCall.cloneForKeys(keys); | ||
| this.trigger('prepareLookupCall', {lookupCall}); |
There was a problem hiding this comment.
The previous implementation in _formatValue() triggered this event including the row property. Because we are now in a batch context, this can no longer be set. However, listeners still may rely on the property. Isn't this a problem?
| * | ||
| * SPDX-License-Identifier: EPL-2.0 | ||
| */ | ||
| import {scout} from '../scout'; |
There was a problem hiding this comment.
Import from index, not from individual files.
| if (arrays.empty(keys)) { | ||
| return $.resolvedPromise(); | ||
| } | ||
|
|
| import {dataObjects} from '../dataobject/dataObjects'; | ||
| import $ from 'jquery'; | ||
|
|
||
| export class BatchCall<TKey, TValue> { |
There was a problem hiding this comment.
Add some documentation? (see old BatchLookup implementation).
| } | ||
|
|
||
| /** | ||
| * Same key might point to the same values! Do not modify (read only!) |
There was a problem hiding this comment.
What is the purpose of this documentation? It does not seem to be related to this method. Modifying the content of keys is never a good idea, is an explicit warning really needed?
0b5fbb1 to
16eff29
Compare
16eff29 to
de239fe
Compare
SmartColumns with batched LookupCall already collected all keys and then updated the cell text as soon as the async LookupCall completed. Therefore, there is only one promise for the LookupCall execution returning the texts for all keys. Unfortunately the SmartColumn then splits this promise into one promise for each cell containing only the value for the specific cell. This leads to thousands of promises on large tables. These promises are collected in the TableUpdateBuffer and removed as they resolve. This operation was O(n^2) as the buffer internally used an array to store the promises. Processing lots of such promises together with the inefficient handling of the buffer leads to unnecessary computation time on the client. Because of some async overhead, executing a promise for each cell is significantly slower than executing one promise handling all cells. On large tables with thousands of rows and multiple SmartColumns this may sum up to several seconds just to process these promises. This commit changes the following: - Introduce a helper class BatchCall to collect keys and using an async batch callback executed for all the keys. - Column uses this new BatchCall in formatValue() if available. This allows each column to support batch processing more easily. Implementors only need to set the _batchFormat member defining the batch callback. - The deferred Cell text update in batch mode always updates all texts of the column instead of only one cell. Accordingly, _formatValue and formatValue() in the async case now return all texts instead of only the one for a single cell. - StaticLookupCall & LookupCallColumn now supports batching to benefit from the new features. 410165, 399939
de239fe to
3ae3ebf
Compare
SmartColumns with batched LookupCall already collected all keys and then updated the cell text as soon as the async LookupCall completed. Therefore, there is only one promise for the LookupCall execution returning the texts for all keys.
Unfortunately the SmartColumn then splits this promise into one promise for each cell containing only the value for the specific cell. This leads to thousands of promises on large tables.
These promises are collected in the TableUpdateBuffer and removed as they resolve. This operation was O(n^2) as the buffer internally used an array to store the promises.
Processing lots of such promises together with the inefficient handling of the buffer leads to unnecessary computation time on the client. Because of some async overhead, executing a promise for each cell is significantly slower than executing one promise handling all cells. On large tables with thousands of rows and multiple SmartColumns this may sum up to several seconds just to process these promises.
This commit changes the following:
410165, 399939