-
Notifications
You must be signed in to change notification settings - Fork 114
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[PERF] point_of_sale: lazy reactive getter
**Problem:** A customer experiences a noticeable delay (1 to 2 sec) when adding product in the cart because of lack of caching computations. It gets worse the more orderlines are added in the cart. **Solution:** This commit introduces an implementation of lazy reactive computed value that is pull-based -- it only recomputes when it's needed. Check the following PR in odoo/owl for its origin: odoo/owl#1499 This can be used for smart-caching selected getters. In this PR, we made a getter for the `get_all_prices` method and instead of normal getter access, we get the result of the getter using the `get` method introduced in the wrapper class. This means that in one call stack, calling `get('allPrices')` will only run once and it will keep returning the cached value until a dependency in its dependency (calculation) graph changed. With this patch, adding an orderline is now 300ms -- approximate 5 times faster than without caching.
- Loading branch information
Showing
4 changed files
with
102 additions
and
11 deletions.
There are no files selected for viewing
82 changes: 82 additions & 0 deletions
82
addons/point_of_sale/static/src/app/models/lazy_computed.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
/** @odoo-module */ | ||
|
||
import { effect } from "@web/core/utils/reactive"; | ||
|
||
function lazyComputed(obj, propName, compute) { | ||
const key = Symbol(propName); | ||
Object.defineProperty(obj, propName, { | ||
get() { | ||
return this[key](); | ||
}, | ||
configurable: true, | ||
}); | ||
|
||
effect( | ||
function recompute(obj) { | ||
const value = []; | ||
obj[key] = () => { | ||
if (!value.length) { | ||
value.push(compute(obj)); | ||
} | ||
return value[0]; | ||
}; | ||
}, | ||
[obj] | ||
); | ||
} | ||
|
||
function getAllGetters(proto) { | ||
const getterNames = new Set(); | ||
const getters = new Set(); | ||
while (proto !== null) { | ||
const descriptors = Object.getOwnPropertyDescriptors(proto); | ||
for (const [name, descriptor] of Object.entries(descriptors)) { | ||
if (descriptor.get && !getterNames.has(name)) { | ||
getterNames.add(name); | ||
getters.add([name, descriptor.get]); | ||
} | ||
} | ||
proto = Object.getPrototypeOf(proto); | ||
} | ||
return getters; | ||
} | ||
|
||
export function createModelWithLazyGetters(Class) { | ||
const getters = new Map(); | ||
let gettersRegistered = false; | ||
/** | ||
* Getters are registered to the auto-caching mechanism of the lazyComputed. | ||
* To use the auto-caching, a getter must be accessed using the `get` method. | ||
*/ | ||
function registerGetters() { | ||
for (const [name, func] of getAllGetters(WithLazyGetters.prototype)) { | ||
if (name.startsWith("__") && name.endsWith("__")) { | ||
continue; | ||
} | ||
const lazyName = `__lazy_${name}`; | ||
getters.set(name, [lazyName, (obj) => func.call(obj)]); | ||
} | ||
gettersRegistered = true; | ||
} | ||
class WithLazyGetters extends Class { | ||
constructor(...args) { | ||
// before creating the first instance, we register the getters | ||
if (!gettersRegistered) { | ||
registerGetters(); | ||
} | ||
super(...args); | ||
for (const [lazyName, func] of getters.values()) { | ||
lazyComputed(this, lazyName, func); | ||
} | ||
} | ||
get(getterName) { | ||
const [lazyName] = getters.get(getterName); | ||
if (lazyName) { | ||
return this[lazyName]; | ||
} else { | ||
throw new Error(`Getter ${getterName} is not defined.`); | ||
} | ||
} | ||
} | ||
return WithLazyGetters; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters