mirror of
https://github.com/bitwarden/browser
synced 2025-12-17 08:43:33 +00:00
Add sequentialize to prevent parralel loading of cipher keys (#7)
* Add sequentialize to prevent parralel loading of cipher keys Massively improves start up performance of extensions * Add tests for sequentialize * Fix sequentialize as it was caching calls for all instances together * Add sequentialize to the functions that have internal caches * Adding sequentialize to getOrgKeys makes big performance difference * Update cipher.service.ts * Update collection.service.ts * Update folder.service.ts
This commit is contained in:
52
src/misc/sequentialize.ts
Normal file
52
src/misc/sequentialize.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Use as a Decorator on async functions, it will prevent multiple 'active' calls as the same time
|
||||
*
|
||||
* If a promise was returned from a previous call to this function, that hasn't yet resolved it will
|
||||
* be returned, instead of calling the original function again
|
||||
*
|
||||
* Results are not cached, once the promise has returned, the next call will result in a fresh call
|
||||
*/
|
||||
export function sequentialize(key: (args: any[]) => string = JSON.stringify) {
|
||||
return (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
|
||||
const originalMethod: () => Promise<any> = descriptor.value;
|
||||
|
||||
const caches = new Map<any, Map<string, Promise<any>>>();
|
||||
const getCache = (obj: any) => {
|
||||
let cache = caches.get(obj);
|
||||
if (cache) {
|
||||
return cache;
|
||||
}
|
||||
cache = new Map<string, Promise<any>>();
|
||||
caches.set(obj, cache);
|
||||
|
||||
return cache;
|
||||
};
|
||||
|
||||
return {
|
||||
value: function(...args: any[]) {
|
||||
const argsKey = key(args);
|
||||
const cache = getCache(this);
|
||||
|
||||
let res = cache.get(argsKey);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
res = originalMethod.apply(this, args)
|
||||
.then((val: any) => {
|
||||
cache.delete(argsKey);
|
||||
|
||||
return val;
|
||||
})
|
||||
.catch((err: any) => {
|
||||
cache.delete(argsKey);
|
||||
|
||||
throw err;
|
||||
});
|
||||
cache.set(argsKey, res);
|
||||
|
||||
return res;
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { EncryptionType } from '../../enums/encryptionType';
|
||||
|
||||
import { CryptoService } from '../../abstractions/crypto.service';
|
||||
|
||||
import { sequentialize } from '../../misc/sequentialize';
|
||||
import { Utils } from '../../misc/utils';
|
||||
|
||||
export class CipherString {
|
||||
@@ -89,6 +90,7 @@ export class CipherString {
|
||||
}
|
||||
}
|
||||
|
||||
@sequentialize((args) => args[0])
|
||||
async decrypt(orgId: string): Promise<string> {
|
||||
if (this.decryptedValue) {
|
||||
return Promise.resolve(this.decryptedValue);
|
||||
|
||||
@@ -11,6 +11,7 @@ import { StorageService } from '../abstractions/storage.service';
|
||||
|
||||
import { ConstantsService } from './constants.service';
|
||||
|
||||
import { sequentialize } from '../misc/sequentialize';
|
||||
import { Utils } from '../misc/utils';
|
||||
|
||||
const Keys = {
|
||||
@@ -164,6 +165,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return this.privateKey;
|
||||
}
|
||||
|
||||
@sequentialize()
|
||||
async getOrgKeys(): Promise<Map<string, SymmetricCryptoKey>> {
|
||||
if (this.orgKeys != null && this.orgKeys.size > 0) {
|
||||
return this.orgKeys;
|
||||
|
||||
Reference in New Issue
Block a user