Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 41 additions & 4 deletions app/client/src/workers/Evaluation/handlers/evalTree.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ConfigTree, DataTree } from "entities/DataTree/dataTreeTypes";
import type ReplayEntity from "entities/Replay";
import ReplayCanvas from "entities/Replay/ReplayEntity/ReplayCanvas";
import { isEmpty } from "lodash";
import { isEmpty, set, get, unset } from "lodash";
import type { DependencyMap, EvalError } from "utils/DynamicBindingUtils";
import { EvalErrorTypes } from "utils/DynamicBindingUtils";
import type { JSUpdate } from "utils/JSPaneUtils";
Expand Down Expand Up @@ -35,6 +35,9 @@ import type { CanvasWidgetsReduxState } from "ee/reducers/entityReducers/canvasW
import type { MetaWidgetsReduxState } from "reducers/entityReducers/metaWidgetsReducer";
import type { Attributes } from "instrumentation/types";
import { updateActionsToEvalTree } from "./updateActionData";
import { create } from "mutative";
import { klona } from "klona";
import { klona as klonaJSON } from "klona/json";

// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down Expand Up @@ -192,6 +195,10 @@ export async function evalTree(
});
staleMetaIds = dataTreeResponse.staleMetaIds;
} else {
dataTreeEvaluator.shouldRunOptimisation = true;
dataTreeEvaluator.setSemiUpdatedPrevTree(
dataTreeEvaluator.getPrevState(),
);
const tree = dataTreeEvaluator.getEvalTree();

// during update cycles update actions to the dataTree directly
Expand Down Expand Up @@ -244,10 +251,40 @@ export async function evalTree(
),
);

dataTree = makeEntityConfigsAsObjProperties(dataTreeEvaluator.evalTree, {
evalProps: dataTreeEvaluator.evalProps,
});
dataTree = create(
dataTreeEvaluator.getSemiUpdatedPrevTree() || {},
(draft: DataTree) => {
for (const fullPropertyPath of evalOrder) {
const value = klonaJSON(
get(dataTreeEvaluator?.evalTree, fullPropertyPath),
);

if (value === undefined) {
unset(draft, fullPropertyPath);
} else {
set(draft, fullPropertyPath, value);
}
}

const evalProps = dataTreeEvaluator?.evalProps;

if (!evalProps) return;

for (const [entityName, entityEvalProps] of Object.entries(
evalProps,
)) {
if (!entityEvalProps.__evaluation__) continue;

set(
draft[entityName as keyof DataTree],
"__evaluation__",
klona({ errors: entityEvalProps.__evaluation__.errors }),
);
}
},
);

dataTreeEvaluator.shouldRunOptimisation = false;
evalMetaUpdates = JSON.parse(
JSON.stringify(updateResponse.evalMetaUpdates),
);
Expand Down
63 changes: 50 additions & 13 deletions app/client/src/workers/Evaluation/handlers/updateActionData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import DataStore from "../dataStore";
import { EVAL_WORKER_SYNC_ACTION } from "ee/workers/Evaluation/evalWorkerActions";
import type { DataTree } from "entities/DataTree/dataTreeTypes";
import type { UpdateActionProps } from "./types";
import { create } from "mutative";
import { klona as klonaJSON } from "klona/json";

export default function (request: EvalWorkerSyncRequest) {
const actionsDataToUpdate: UpdateActionProps[] = request.data;
Expand Down Expand Up @@ -45,22 +47,57 @@ export function updateActionsToEvalTree(
) {
if (!actionsToUpdate) return;

for (const actionToUpdate of actionsToUpdate) {
const { dataPath, dataPathRef, entityName } = actionToUpdate;
let { data } = actionToUpdate;
if (!dataTreeEvaluator?.shouldRunOptimisation) {
for (const actionToUpdate of actionsToUpdate) {
const { dataPath, dataPathRef, entityName } = actionToUpdate;
let { data } = actionToUpdate;

if (dataPathRef) {
data = DataStore.getActionData(dataPathRef);
DataStore.deleteActionData(dataPathRef);
if (dataPathRef) {
data = DataStore.getActionData(dataPathRef);
DataStore.deleteActionData(dataPathRef);
}

// update the evaltree
set(evalTree, `${entityName}.[${dataPath}]`, data);
// Update context
set(self, `${entityName}.[${dataPath}]`, data);
// Update the datastore
const path = `${entityName}.${dataPath}`;

DataStore.setActionData(path, data);
}
} else {
const updatedPrevState = create(
dataTreeEvaluator.getSemiUpdatedPrevTree(),
(draft) => {
for (const actionToUpdate of actionsToUpdate) {
const { dataPath, dataPathRef, entityName } = actionToUpdate;
let { data } = actionToUpdate;

if (dataPathRef) {
data = DataStore.getActionData(dataPathRef);
DataStore.deleteActionData(dataPathRef);
}

// update the evaltree
set(evalTree, `${entityName}.[${dataPath}]`, data);

if (draft) {
set(draft, `${entityName}.[${dataPath}]`, klonaJSON(data));
}

// Update context
set(self, `${entityName}.[${dataPath}]`, data);
// Update the datastore
const path = `${entityName}.${dataPath}`;

DataStore.setActionData(path, data);
}
},
);

// update the evaltree
set(evalTree, `${entityName}.[${dataPath}]`, data);
// Update context
set(self, `${entityName}.[${dataPath}]`, data);
// Update the datastore
const path = `${entityName}.${dataPath}`;
dataTreeEvaluator.setSemiUpdatedPrevTree(updatedPrevState);

DataStore.setActionData(path, data);
// Update setSemiUpdatedPrevTree with mutative
}
}
117 changes: 108 additions & 9 deletions app/client/src/workers/common/DataTreeEvaluator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ import { WorkerEnv } from "workers/Evaluation/handlers/workerEnv";
import type { WebworkerSpanData, Attributes } from "instrumentation/types";
import type { AffectedJSObjects } from "actions/EvaluationReduxActionTypes";
import type { UpdateActionProps } from "workers/Evaluation/handlers/types";
import { create } from "mutative";

type SortedDependencies = Array<string>;
export interface EvalProps {
Expand Down Expand Up @@ -194,6 +195,8 @@ export default class DataTreeEvaluator {
undefinedEvalValuesMap: Record<string, boolean> = {};

prevState = {};
semiUpdatedPrevState: DataTree | null = null;

// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
setPrevState(state: any) {
Expand All @@ -216,7 +219,15 @@ export default class DataTreeEvaluator {
getEvalTree() {
return this.evalTree;
}
shouldRunOptimisation = false;

getSemiUpdatedPrevTree() {
return this.semiUpdatedPrevState;
}

setSemiUpdatedPrevTree(dataTree: DataTree | null) {
this.semiUpdatedPrevState = dataTree;
}
getEvalProps() {
return this.evalProps;
}
Expand Down Expand Up @@ -791,21 +802,52 @@ export default class DataTreeEvaluator {
// Remove anything from the sort order that is not a dynamic leaf since only those need evaluation
const evaluationOrder: string[] = [];

for (const fullPath of subTreeSortOrder) {
if (pathsToSkipFromEval.includes(fullPath)) continue;
const semiUpdatedPrevTree = this.getSemiUpdatedPrevTree();

if (this.shouldRunOptimisation) {
const updatedSemiUpdatedPrevTree = create<DataTree>(
semiUpdatedPrevTree || {},
(draft: DataTree) => {
for (const fullPath of subTreeSortOrder) {
if (pathsToSkipFromEval.includes(fullPath)) continue;

if (!isDynamicLeaf(unEvalTree, fullPath, this.getConfigTree()))
continue;

const evalPropValue = get(this.evalTree, fullPath);

evaluationOrder.push(fullPath);

if (isFunction(evalPropValue)) continue;

const unEvalPropValue = get(unEvalTree, fullPath);

// Set all values from unEvalTree to the evalTree to run evaluation for unevaluated values.
set(this.evalTree, fullPath, klona(unEvalPropValue));
set(draft, fullPath, klona(unEvalPropValue));
}
},
);

if (!isDynamicLeaf(unEvalTree, fullPath, this.getConfigTree())) continue;
this.setSemiUpdatedPrevTree(updatedSemiUpdatedPrevTree);
} else {
for (const fullPath of subTreeSortOrder) {
if (pathsToSkipFromEval.includes(fullPath)) continue;

if (!isDynamicLeaf(unEvalTree, fullPath, this.getConfigTree()))
continue;

const evalPropValue = get(this.evalTree, fullPath);
const evalPropValue = get(this.evalTree, fullPath);

evaluationOrder.push(fullPath);
evaluationOrder.push(fullPath);

if (isFunction(evalPropValue)) continue;
if (isFunction(evalPropValue)) continue;

const unEvalPropValue = get(unEvalTree, fullPath);
const unEvalPropValue = get(unEvalTree, fullPath);

// Set all values from unEvalTree to the evalTree to run evaluation for unevaluated values.
set(this.evalTree, fullPath, klona(unEvalPropValue));
// Set all values from unEvalTree to the evalTree to run evaluation for unevaluated values.
set(this.evalTree, fullPath, klona(unEvalPropValue));
}
}

return { evaluationOrder };
Expand All @@ -831,6 +873,27 @@ export default class DataTreeEvaluator {

updateEvalTreeWithJSCollectionState(this.evalTree);

if (this.shouldRunOptimisation) {
const jsCollections = JSObjectCollection.getVariableState();
const jsCollectionEntries = Object.entries(jsCollections);

const updatedSemiUpdatedPrevTree = create<DataTree>(
this.getSemiUpdatedPrevTree() || {},
(draft: DataTree) => {
for (const [jsObjectName, variableState] of jsCollectionEntries) {
if (!draft[jsObjectName]) continue;

draft[jsObjectName] = Object.assign(
draft[jsObjectName],
klonaJSON(variableState),
);
}
},
);

this.setSemiUpdatedPrevTree(updatedSemiUpdatedPrevTree);
}

const calculateSortOrderStartTime = performance.now();
const subTreeSortOrder = this.calculateSubTreeSortOrder(
updatedValuePaths,
Expand All @@ -854,6 +917,19 @@ export default class DataTreeEvaluator {
evaluationOrder: evaluationOrder,
});

if (this.shouldRunOptimisation) {
const updatedSemiUpdatedPrevTree1 = create<DataTree>(
this.getSemiUpdatedPrevTree() || {},
(draft: DataTree) => {
removedPaths.forEach((removedPath) => {
unset(draft, removedPath.fullpath);
});
},
);

this.setSemiUpdatedPrevTree(updatedSemiUpdatedPrevTree1);
}

// Remove any deleted paths from the eval tree
removedPaths.forEach((removedPath) => {
unset(this.evalTree, removedPath.fullpath);
Expand Down Expand Up @@ -1796,12 +1872,35 @@ export default class DataTreeEvaluator {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
differences: Diff<any, any>[];
}) {
if (differences.length === 0) return;

// Apply changes to this.evalTree
for (const d of differences) {
if (!Array.isArray(d.path) || d.path.length === 0) continue; // Null check for typescript

// Apply the changes into the evalTree so that it gets the latest changes
applyChange(this.evalTree, undefined, d);
}

if (this.shouldRunOptimisation) {
// Update the semi-updated previous state with the same differences
const semiUpdatedPrevState = this.getSemiUpdatedPrevTree();

if (semiUpdatedPrevState) {
const updatedSemiPrevState = create<DataTree>(
semiUpdatedPrevState || {},
(draft: DataTree) => {
for (const d of differences) {
if (!Array.isArray(d.path) || d.path.length === 0) continue;

applyChange(draft, undefined, klona(d));
}
},
);

this.setSemiUpdatedPrevTree(updatedSemiPrevState);
}
}
}

calculateSubTreeSortOrder(
Expand Down
Loading