Introduce shallow serialization

Summary:
Changelog: [Flipper] Improve serialisation mechanism format & speed

The default serialisation mechanism used by Flipper to serialise plugin states is very flexible, taking care of maps, sets, dates etc. However, it is also really slow, leading to issues like in the related tasks, and work arounds like D17402443 (98bc01618f) to skip the whole process for plugins.

This diff changes the serialisation mechanism to have a better trade off between speed and convenience: For now we will only apply the smart serialisation for objects living at the _root_ of the serialised object, but it won't be applied recursively.

This sounds like a dangerous change, but works well in practice:
* I went through all `persistedState` and `createState` definition (the types), and the idea that complex types like Map and Set only live at the root of the persisted state holds up nicely. That makes sense as well since plugins typically store literally the same data as that they have received over the wire, except that they put it in some maps, sets etc.
* I introduced `assertSerializable` that only runs in dev/test, which will check (recursively, but without all the cloning) to see if a tree is indeed serialisable.
* The fact that by swapping this mechanism rarely existing unit test for exportData needed changes proves that the assumption that only roots are relevant generally upholds (or that plugin authors don't write enough tests ;-)).
* I verified that popular plugins still import / export correctly (actually *more* plugins are exportable now than before, thanks to sandy wrapper introduced earlier)

Reviewed By: jknoxville

Differential Revision: D29327499

fbshipit-source-id: 0ff17d9c5eb68fccfc2937b634cfa8f4f924247d
This commit is contained in:
Michel Weststrate
2021-06-29 08:02:53 -07:00
committed by Facebook GitHub Bot
parent aff02b2ca1
commit 279f3c41b7
9 changed files with 503 additions and 37 deletions

View File

@@ -49,7 +49,6 @@ import {getPluginTitle, isSandyPlugin} from './pluginUtils';
import {capture} from './screenshot';
import {uploadFlipperMedia} from '../fb-stubs/user';
import {Idler} from 'flipper-plugin';
import {deserializeObject, makeObjectSerializable} from './serialization';
export const IMPORT_FLIPPER_TRACE_EVENT = 'import-flipper-trace';
export const EXPORT_FLIPPER_TRACE_EVENT = 'export-flipper-trace';
@@ -264,15 +263,14 @@ async function exportSandyPluginStates(
if (!res[client.id]) {
res[client.id] = {};
}
// makeObjectSerializable is slow but very convenient by default. If people want to speed things up
res[client.id][pluginId] = await makeObjectSerializable(
await client.sandyPluginStates
try {
res[client.id][pluginId] = await client.sandyPluginStates
.get(pluginId)!
.exportState(idler, statusUpdate),
idler,
statusUpdate,
'Serializing plugin: ' + pluginId,
);
.exportState(idler, statusUpdate);
} catch (error) {
console.error('Error while serializing plugin ' + pluginId, error);
throw new Error(`Failed to serialize plugin ${pluginId}: ${error}`);
}
}
}
return res;
@@ -461,11 +459,10 @@ export async function processStore(
idler,
);
const devicePluginStates = await makeObjectSerializable(
await device.exportState(idler, statusUpdate, selectedPlugins),
const devicePluginStates = await device.exportState(
idler,
statusUpdate,
'Serializing device plugins',
selectedPlugins,
);
statusUpdate('Uploading screenshot...');
@@ -800,7 +797,7 @@ export function importDataToStore(source: string, data: string, store: Store) {
archivedDevice.loadDevicePlugins(
store.getState().plugins.devicePlugins,
store.getState().connections.enabledDevicePlugins,
deserializeObject(device.pluginStates),
device.pluginStates,
);
store.dispatch({
type: 'REGISTER_DEVICE',
@@ -829,9 +826,7 @@ export function importDataToStore(source: string, data: string, store: Store) {
});
clients.forEach((client: {id: string; query: ClientQuery}) => {
const sandyPluginStates = deserializeObject(
json.pluginStates2[client.id] || {},
);
const sandyPluginStates = json.pluginStates2[client.id] || {};
const clientPlugins: Array<string> = [
...keys
.filter((key) => {