Skip to content
This repository was archived by the owner on May 5, 2021. It is now read-only.

Commit 62dcd7e

Browse files
Gorashdmo-odoo
authored andcommitted
[FIX] Core/Memory: snapshot should improve performance without corruption
1 parent 095015b commit 62dcd7e

File tree

2 files changed

+222
-11
lines changed

2 files changed

+222
-11
lines changed

packages/core/src/Memory/Memory.ts

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ export function markAsDiffRoot(obj: MemoryAllowedObjectType): void {
124124

125125
let memoryID = 0;
126126
const memoryRootSliceName = '';
127+
const regExpoSnapshotOrigin = /^.*\[snapshot from (.*)\]$/;
127128

128129
export class MemorySlice {
129130
name: SliceKey;
@@ -554,6 +555,53 @@ export class Memory {
554555
}
555556
return true;
556557
}
558+
/**
559+
* Debug tools to get the list of memory slice and changes from a
560+
* versionable in order to be able to trace his evolution.
561+
*
562+
* @param versionable
563+
*/
564+
getSliceAndChanges(
565+
versionable: object | number | VersionableID,
566+
): { sliceKey: string; versionableId: number; changes: MemoryType }[] {
567+
let id: number;
568+
if (typeof versionable === 'number' || versionable instanceof VersionableID) {
569+
id = +versionable;
570+
versionable = this._proxies[id];
571+
} else {
572+
for (const key in this._proxies) {
573+
if (this._proxies[key] === versionable) {
574+
id = +key;
575+
break;
576+
}
577+
}
578+
}
579+
const res: { sliceKey: string; versionableId: number; changes: MemoryType }[] = [];
580+
const slices = Object.values(this._slices).filter(slice => slice.data[id]);
581+
for (const slice of slices) {
582+
let changes: MemoryType;
583+
if (versionable instanceof Set) {
584+
const data = slice.data[id] as MemoryTypeSet;
585+
changes = {
586+
add: new Set(data.add),
587+
delete: new Set(data.delete),
588+
};
589+
} else if (versionable instanceof Array) {
590+
const data = slice.data[id] as MemoryTypeArray;
591+
changes = {
592+
props: Object.assign({}, data.props),
593+
patch: Object.assign({}, data.patch),
594+
};
595+
} else {
596+
const data = slice.data[id] as MemoryTypeObject;
597+
changes = {
598+
props: Object.assign({}, data.props),
599+
};
600+
}
601+
res.push({ sliceKey: slice.name, versionableId: id, changes: changes });
602+
}
603+
return res;
604+
}
557605

558606
/////////////////////////////////////////////////////
559607
// private
@@ -867,16 +915,18 @@ export class Memory {
867915
Object.keys(slice).forEach(ID => {
868916
const id = +ID;
869917
const memoryItem = slice[id];
870-
if (!intoSlices[id]) {
871-
intoSlices[id] = memoryItem;
872-
return;
873-
}
874918
if (memoryItem instanceof MemoryTypeArray) {
875-
const intoItem = intoSlices[id] as MemoryTypeArray;
919+
let intoItem = intoSlices[id] as MemoryTypeArray;
920+
if (!intoItem) {
921+
intoItem = intoSlices[id] = new MemoryTypeArray();
922+
}
876923
Object.assign(intoItem.patch, memoryItem.patch);
877924
Object.assign(intoItem.props, memoryItem.props);
878925
} else if (memoryItem instanceof MemoryTypeSet) {
879-
const intoItem = intoSlices[id] as MemoryTypeSet;
926+
let intoItem = intoSlices[id] as MemoryTypeSet;
927+
if (!intoItem) {
928+
intoItem = intoSlices[id] = new MemoryTypeSet();
929+
}
880930
memoryItem.add.forEach(item => {
881931
if (!intoItem.delete.has(item)) {
882932
intoItem.add.add(item);
@@ -892,7 +942,10 @@ export class Memory {
892942
}
893943
});
894944
} else {
895-
const intoItem = intoSlices[id] as MemoryTypeObject;
945+
let intoItem = intoSlices[id] as MemoryTypeObject;
946+
if (!intoItem) {
947+
intoItem = intoSlices[id] = new MemoryTypeObject();
948+
}
896949
Object.assign(intoItem.props, memoryItem.props);
897950
}
898951
});
@@ -908,7 +961,11 @@ export class Memory {
908961
if (refs.length > this._numberOfFlatSlices + this._numberOfSlicePerSnapshot) {
909962
const fromSliceKey = refs[refs.length - 1].name;
910963
const unitSliceKey = refs[refs.length - 1 - this._numberOfSlicePerSnapshot].name;
911-
const newSliceKey = unitSliceKey + '[snapshot from ' + fromSliceKey + ']';
964+
const newSliceKey =
965+
unitSliceKey +
966+
'[snapshot from ' +
967+
fromSliceKey.replace(regExpoSnapshotOrigin, '$1') +
968+
']';
912969
this.snapshot(fromSliceKey, unitSliceKey, newSliceKey);
913970
}
914971
}

packages/core/src/Memory/test/memory.test.ts

Lines changed: 157 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2273,7 +2273,7 @@ describe('core', () => {
22732273

22742274
expect(Object.keys(memory._slices.snap.data).length).to.equal(1);
22752275
patch = memory._slices.snap.data[ID];
2276-
expect(Object.values(patch.patch)).to.deep.equal([1, 2, 3]);
2276+
expect(Object.values(patch.patch).sort()).to.deep.equal([1, 2, 3]);
22772277
});
22782278
it('snapshot set changes', () => {
22792279
const memory = new Memory();
@@ -2316,7 +2316,7 @@ describe('core', () => {
23162316

23172317
const add = [];
23182318
patch.add.forEach((item: number) => add.push(item));
2319-
expect(add).to.deep.equal([1, 3]);
2319+
expect(add.sort()).to.deep.equal([1, 3]);
23202320
const remove = [];
23212321
patch.delete.forEach((item: number) => remove.push(item));
23222322
expect(remove).to.deep.equal([0]);
@@ -2421,10 +2421,164 @@ describe('core', () => {
24212421
expect(Object.keys(memory._slices).length).to.equal(4);
24222422
expect(Object.keys(memory._slices.test.data).length).to.equal(2);
24232423
patch = memory._slices.test.data[ID];
2424-
expect(Object.values(patch.patch)).to.deep.equal([1, 2, 3]);
2424+
expect(Object.values(patch.patch).sort()).to.deep.equal([1, 2, 3]);
24252425
patch = memory._slices.test.data[obj[memoryProxyPramsKey].ID];
24262426
expect(patch).to.deep.equal({ props: { 'x+y': 3 } });
24272427
});
2428+
it('snapshop create some slice and concat data', () => {
2429+
const memory = new Memory();
2430+
memory._numberOfFlatSlices = 5;
2431+
memory._numberOfSlicePerSnapshot = 3;
2432+
2433+
memory.create('test');
2434+
memory.switchTo('test');
2435+
2436+
const object = new VersionableObject();
2437+
memory.attach(object);
2438+
2439+
object['x+'] = 1;
2440+
2441+
for (let i = 0; i < 10; i++) {
2442+
memory.create(i.toString());
2443+
memory.switchTo(i.toString());
2444+
}
2445+
2446+
object['y+'] = 2;
2447+
2448+
for (let i = 10; i < 20; i++) {
2449+
memory.create(i.toString());
2450+
memory.switchTo(i.toString());
2451+
}
2452+
2453+
const sliceAndChanges = memory.getSliceAndChanges(object);
2454+
expect(sliceAndChanges.length).to.equal(
2455+
6,
2456+
'creation in "test", change in "9" and 4 snapshots',
2457+
);
2458+
2459+
for (const c of sliceAndChanges) {
2460+
switch (c.sliceKey) {
2461+
case 'test':
2462+
case '2[snapshot from test]':
2463+
case '5[snapshot from test]':
2464+
case '8[snapshot from test]':
2465+
expect(c.changes).to.deep.equal({ props: { 'x+': 1 } }, c.sliceKey);
2466+
break;
2467+
case '9':
2468+
expect(c.changes).to.deep.equal({ props: { 'y+': 2 } }, c.sliceKey);
2469+
break;
2470+
case '11[snapshot from test]':
2471+
expect(c.changes).to.deep.equal(
2472+
{ props: { 'x+': 1, 'y+': 2 } },
2473+
c.sliceKey,
2474+
);
2475+
break;
2476+
default:
2477+
expect(true).to.equal(false);
2478+
}
2479+
}
2480+
});
2481+
it('undo the slice after a snapshot of added prop of object', () => {
2482+
const memory = new Memory();
2483+
memory._numberOfFlatSlices = 5;
2484+
memory._numberOfSlicePerSnapshot = 3;
2485+
2486+
memory.create('test');
2487+
memory.switchTo('test');
2488+
2489+
const object = new VersionableObject();
2490+
memory.attach(object);
2491+
2492+
object['x+'] = 1;
2493+
2494+
for (let i = 0; i < 10; i++) {
2495+
memory.create(i.toString());
2496+
memory.switchTo(i.toString());
2497+
}
2498+
2499+
object['y+'] = 2;
2500+
2501+
for (let i = 10; i < 20; i++) {
2502+
memory.create(i.toString());
2503+
memory.switchTo(i.toString());
2504+
}
2505+
2506+
memory.switchTo('15');
2507+
expect(object).to.deep.equal({ 'x+': 1, 'y+': 2 });
2508+
2509+
memory.switchTo('1');
2510+
expect(object).to.deep.equal(
2511+
{ 'x+': 1 },
2512+
'Should removed the last created props',
2513+
);
2514+
});
2515+
it('undo the slice after a snapshot of added item in array', () => {
2516+
const memory = new Memory();
2517+
memory._numberOfFlatSlices = 5;
2518+
memory._numberOfSlicePerSnapshot = 3;
2519+
2520+
memory.create('test');
2521+
memory.switchTo('test');
2522+
2523+
const array = new VersionableArray();
2524+
memory.attach(array);
2525+
2526+
const object1 = new VersionableObject({ id: '1' });
2527+
array[1] = object1;
2528+
2529+
for (let i = 0; i < 10; i++) {
2530+
memory.create(i.toString());
2531+
memory.switchTo(i.toString());
2532+
}
2533+
2534+
const object2 = new VersionableObject({ id: '3' });
2535+
array[3] = object2;
2536+
2537+
for (let i = 10; i < 20; i++) {
2538+
memory.create(i.toString());
2539+
memory.switchTo(i.toString());
2540+
}
2541+
2542+
memory.switchTo('15');
2543+
expect(array).to.deep.equal([undefined, { id: '1' }, undefined, { id: '3' }]);
2544+
2545+
memory.switchTo('1');
2546+
expect(array).to.deep.equal(
2547+
[undefined, { id: '1' }],
2548+
'Should removed the last created items',
2549+
);
2550+
});
2551+
it('undo the slice after a snapshot of added item in set', () => {
2552+
const memory = new Memory();
2553+
memory._numberOfFlatSlices = 5;
2554+
memory._numberOfSlicePerSnapshot = 3;
2555+
2556+
memory.create('test');
2557+
memory.switchTo('test');
2558+
2559+
const set = new VersionableSet();
2560+
memory.attach(set);
2561+
2562+
set.add(1);
2563+
2564+
for (let i = 0; i < 10; i++) {
2565+
memory.create(i.toString());
2566+
memory.switchTo(i.toString());
2567+
}
2568+
2569+
set.add(2);
2570+
2571+
for (let i = 10; i < 20; i++) {
2572+
memory.create(i.toString());
2573+
memory.switchTo(i.toString());
2574+
}
2575+
2576+
memory.switchTo('15');
2577+
expect([...set]).to.deep.equal([1, 2]);
2578+
2579+
memory.switchTo('1');
2580+
expect([...set]).to.deep.equal([1], 'Should removed the last added items');
2581+
});
24282582
});
24292583

24302584
describe('root & path & changes', () => {

0 commit comments

Comments
 (0)