Skip to content

Commit

Permalink
chore: adding telemetry for klona (#35918)
Browse files Browse the repository at this point in the history
## Description
Klona operations are generally expensive, for that reason we will be
tracking the performance of all klonas in the main thread.

## Automation

/ok-to-test tags="@tag.All"

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/10608431935>
> Commit: cab2716
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=10608431935&attempt=2"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.All`
> Spec:
> <hr>Thu, 29 Aug 2024 06:26:43 UTC
<!-- end of auto-generated comment: Cypress test results  -->


## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [ ] No


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit


## Summary by CodeRabbit

- **New Features**
- Introduced telemetry tracking for cloning operations across various
components, enhancing performance monitoring and debugging capabilities.

- **Bug Fixes**
- Improved observability of cloning operations, aiding in identifying
potential performance bottlenecks.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
vsvamsi1 authored Aug 29, 2024
1 parent 3526696 commit 7cdee52
Show file tree
Hide file tree
Showing 28 changed files with 293 additions and 81 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import styled from "styled-components";
import TreeStructure from "components/utils/TreeStructure";
import { Text, Icon, Button, Tooltip } from "@appsmith/ads";
import { klona } from "klona/lite";
import React, { useCallback, useEffect } from "react";
import { ActionCreatorContext } from "../..";
import { AppsmithFunction } from "../../constants";
Expand All @@ -13,6 +12,7 @@ import AnalyticsUtil from "ee/utils/AnalyticsUtil";
import { getActionTypeLabel } from "../ActionBlockTree/utils";
import classNames from "classnames";
import type { AdditionalDynamicDataTree } from "utils/autocomplete/customTreeTypeDefCreator";
import { klonaLiteWithTelemetry } from "utils/helpers";

const CallbackBlockContainer = styled.div<{
isSelected: boolean;
Expand Down Expand Up @@ -108,7 +108,12 @@ export default function ActionTree(props: {
selectBlock(`${id}_success_${blocks.length - 1}`);
return;
}
const newActionBlock = klona(actionBlock);

const newActionBlock = klonaLiteWithTelemetry(
actionBlock,
"ActionTree.handleAddSuccessBlock",
);

newActionBlock.success.blocks.push({
...EMPTY_ACTION_BLOCK,
type: lastAction?.type || "then",
Expand All @@ -127,7 +132,11 @@ export default function ActionTree(props: {
selectBlock(`${id}_failure_${blocks.length - 1}`);
return;
}
const newActionBlock = klona(actionBlock);
const newActionBlock = klonaLiteWithTelemetry(
actionBlock,
"ActionTree.handleAddErrorBlock",
);

newActionBlock.error.blocks.push({
...EMPTY_ACTION_BLOCK,
type: lastAction?.type || "catch",
Expand Down Expand Up @@ -272,7 +281,11 @@ export default function ActionTree(props: {
childActionBlock: TActionBlock,
del?: boolean,
) => {
const newActionBlock = klona(actionBlock);
const newActionBlock = klonaLiteWithTelemetry(
actionBlock,
"ActionTree.onChange",
);

const blocks =
blockType === "failure"
? newActionBlock.error.blocks
Expand Down
11 changes: 10 additions & 1 deletion app/client/src/components/formControls/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { getType, Types } from "utils/TypeHelpers";
import { FIELD_REQUIRED_ERROR, createMessage } from "ee/constants/messages";
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";
import { InputTypes } from "components/constants";
import { startAndEndSpanForFn } from "UITelemetry/generateTraces";

// This function checks if the form is dirty
// We needed this in the cases where datasources are created from APIs and the initial value
Expand Down Expand Up @@ -682,7 +683,15 @@ export const updateEvaluatedSectionConfig = (

// leaving the commented code as a reminder of the above observation.
// const updatedSection = { ...section };
const updatedSection = klona(section);

const updatedSection = startAndEndSpanForFn(
"klona",
{
codeSegment: "utils.updateEvaluatedSectionConfig",
},
() => klona(section),
);

let evaluatedConfig: FormConfigEvalObject = {};
if (
conditionalOutput.hasOwnProperty("evaluateFormConfig") &&
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from "react";
import log from "loglevel";
import { klona } from "klona";
import { isEmpty, isString, maxBy, set, sortBy } from "lodash";

import type { ControlProps } from "./BaseControl";
Expand All @@ -26,6 +25,8 @@ import {
extraSpace,
} from "widgets/JSONFormWidget/constants";

import { klonaRegularWithTelemetry } from "utils/helpers";

type DroppableItem = BaseItemProps & {
index: number;
isCustomField: boolean;
Expand Down Expand Up @@ -165,8 +166,12 @@ class FieldConfigurationControl extends BaseControl<ControlProps, State> {
* the new added paths gets into the dynamicBindingPathList until
* the updateProperty function is fixed.
*/

const updatedSchema = {
schema: klona(widgetProperties.schema),
schema: klonaRegularWithTelemetry(
widgetProperties.schema,
"FieldConfigurationControl.addNewField",
),
};
set(updatedSchema, path, schemaItem);

Expand All @@ -187,7 +192,10 @@ class FieldConfigurationControl extends BaseControl<ControlProps, State> {

updateItems = (items: DroppableItem[]) => {
const { propertyName, propertyValue } = this.props;
const clonedSchema: Schema = klona(propertyValue);
const clonedSchema: Schema = klonaRegularWithTelemetry(
propertyValue,
"FieldConfigurationControl.updateItems",
);

items.forEach((item, index) => {
clonedSchema[item.id].position = index;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import type {
WidgetEntityConfig,
PropertyOverrideDependency,
} from "ee/entities/DataTree/types";
import { klona } from "klona";
import type { MetaState, WidgetMetaState } from ".";
import type { ReduxAction } from "ee/constants/ReduxActionConstants";
import type { EvalMetaUpdates } from "ee/workers/common/DataTreeEvaluator/types";
import produce from "immer";
import { set, unset } from "lodash";
import { klonaRegularWithTelemetry } from "utils/helpers";

export function getMetaWidgetResetObj(
evaluatedWidget: WidgetEntity | undefined,
Expand All @@ -28,7 +28,11 @@ export function getMetaWidgetResetObj(
dependency.DEFAULT && evaluatedWidget[dependency.DEFAULT];
if (defaultPropertyValue !== undefined) {
// cloning data to avoid mutation
resetMetaObj[propertyName] = klona(defaultPropertyValue);

resetMetaObj[propertyName] = klonaRegularWithTelemetry(
defaultPropertyValue,
"metaReducerUtils.getMetaWidgetResetObj",
);
}
});
}
Expand All @@ -48,7 +52,10 @@ export function setMetaValuesOnResetFromEval(

if (!evalMetaUpdates.length) return state;

const newMetaState = klona(state);
const newMetaState = klonaRegularWithTelemetry(
state,
"metaReducerUtils.setMetaValuesOnResetFromEval",
);

evalMetaUpdates.forEach(({ metaPropertyPath, value, widgetId }) => {
if (value === undefined) {
Expand Down
13 changes: 10 additions & 3 deletions app/client/src/sagas/ApiPaneSagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ import { getCurrentBasePageId } from "selectors/editorSelectors";
import { validateResponse } from "./ErrorSagas";
import type { CreateDatasourceSuccessAction } from "actions/datasourceActions";
import { removeTempDatasource } from "actions/datasourceActions";
import { klona } from "klona/lite";
import { toast } from "@appsmith/ads";
import type { AutoGeneratedHeader } from "pages/Editor/APIEditor/helpers";
import { deriveAutoGeneratedHeaderState } from "pages/Editor/APIEditor/helpers";
Expand All @@ -85,6 +84,7 @@ import { DEFAULT_CREATE_APPSMITH_AI_CONFIG } from "constants/ApiEditorConstants/
import { checkAndGetPluginFormConfigsSaga } from "./PluginSagas";
import { convertToBasePageIdSelector } from "selectors/pageListSelectors";
import type { ApplicationPayload } from "entities/Application";
import { klonaLiteWithTelemetry } from "utils/helpers";

function* syncApiParamsSaga(
actionPayload: ReduxActionWithMeta<string, { field: string }>,
Expand Down Expand Up @@ -157,7 +157,10 @@ function* handleUpdateBodyContentType(
);

// get headers
const headers = klona(values?.actionConfiguration?.headers);
const headers = klonaLiteWithTelemetry(
values?.actionConfiguration?.headers,
"ApiPaneSagas.handleUpdateBodyContentType.headers",
);

// set autoGeneratedHeaders
const autoGeneratedHeaders: AutoGeneratedHeader[] = [];
Expand Down Expand Up @@ -223,7 +226,11 @@ function* handleUpdateBodyContentType(
});

// help to prevent cyclic dependency error in case the bodyFormData is empty.
const bodyFormData = klona(values?.actionConfiguration?.bodyFormData);

const bodyFormData = klonaLiteWithTelemetry(
values?.actionConfiguration?.bodyFormData,
"ApiPaneSagas.handleUpdateBodyContentType.bodyFormData",
);

if (
displayFormatValue === POST_BODY_FORMAT_OPTIONS.FORM_URLENCODED ||
Expand Down
13 changes: 10 additions & 3 deletions app/client/src/sagas/DatasourcesSagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,11 @@ import { isDynamicValue } from "utils/DynamicBindingUtils";
import { getQueryParams } from "utils/URLUtils";
import type { GenerateCRUDEnabledPluginMap, Plugin } from "api/PluginApi";
import { getIsGeneratePageInitiator } from "utils/GenerateCrudUtil";
import { shouldBeDefined, trimQueryString } from "utils/helpers";
import {
klonaLiteWithTelemetry,
shouldBeDefined,
trimQueryString,
} from "utils/helpers";
import { updateReplayEntity } from "actions/pageActions";
import OAuthApi from "api/OAuthApi";
import type { AppState } from "ee/reducers";
Expand Down Expand Up @@ -161,7 +165,6 @@ import {
isGoogleSheetPluginDS,
} from "utils/editorContextUtils";
import { getDefaultEnvId } from "ee/api/ApiUtils";
import { klona } from "klona/lite";
import {
getCurrentEditingEnvironmentId,
getCurrentEnvironmentDetails,
Expand Down Expand Up @@ -1780,7 +1783,11 @@ function* filePickerActionCallbackSaga(
getDatasource,
datasourceId,
);
const datasource: Datasource = klona(datasourceFromState);
const datasource: Datasource = klonaLiteWithTelemetry(
datasourceFromState,
"DatasourcesSagas.filePickerActionCallbackSaga",
);

const plugin: Plugin = yield select(getPlugin, datasource?.pluginId);
const applicationId: string = yield select(getCurrentApplicationId);
const pageId: string = yield select(getCurrentPageId);
Expand Down
8 changes: 6 additions & 2 deletions app/client/src/sagas/FormEvaluationSaga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import { getDataTreeActionConfigPath } from "entities/Action/actionProperties";
import { getDataTree } from "selectors/dataTreeSelectors";
import { getDynamicBindings, isDynamicValue } from "utils/DynamicBindingUtils";
import get from "lodash/get";
import { klona } from "klona/lite";
import type { DataTree } from "entities/DataTree/dataTreeTypes";
import {
extractFetchDynamicValueFormConfigs,
Expand All @@ -31,6 +30,7 @@ import type { DatasourceConfiguration } from "entities/Datasource";
import { buffers } from "redux-saga";
import type { Plugin } from "api/PluginApi";
import { doesPluginRequireDatasource } from "ee/entities/Engine/actionHelpers";
import { klonaLiteWithTelemetry } from "utils/helpers";

export interface FormEvalActionPayload {
formId: string;
Expand Down Expand Up @@ -68,10 +68,14 @@ function* setFormEvaluationSagaAsync(
const fetchDynamicValueFormConfigs = extractFetchDynamicValueFormConfigs(
workerResponse[action?.payload?.formId],
);

yield put({
type: ReduxActionTypes.INIT_TRIGGER_VALUES,
payload: {
[action?.payload?.formId]: klona(fetchDynamicValueFormConfigs),
[action?.payload?.formId]: klonaLiteWithTelemetry(
fetchDynamicValueFormConfigs,
"FormEvaluationSaga.setFormEvaluationSagaAsync",
),
},
});
}
Expand Down
8 changes: 6 additions & 2 deletions app/client/src/sagas/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import set from "lodash/set";
import log from "loglevel";
import { isPlainObject, isString } from "lodash";
import { DATA_BIND_REGEX_GLOBAL } from "constants/BindingsConstants";
import { klona } from "klona/lite";
import { apiFailureResponseInterceptor } from "ee/api/ApiUtils";
import { klonaLiteWithTelemetry } from "utils/helpers";

// function to extract all objects that have dynamic values
export const extractFetchDynamicValueFormConfigs = (
Expand Down Expand Up @@ -141,7 +141,11 @@ export const enhanceRequestPayloadWithEventData = (
try {
switch (type) {
case ReduxActionTypes.COPY_ACTION_INIT:
const actionObject = klona(payload) as Action;
const actionObject = klonaLiteWithTelemetry(
payload,
"helpers.enhanceRequestPayloadWithEventData",
) as Action;

const path = `${RequestPayloadAnalyticsPath}.originalActionId`;
const originalActionId = get(actionObject, path, actionObject.id);
if (originalActionId !== undefined)
Expand Down
40 changes: 38 additions & 2 deletions app/client/src/utils/helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ import { getContainerIdForCanvas } from "sagas/WidgetOperationUtils";
import scrollIntoView from "scroll-into-view-if-needed";
import validateColor from "validate-color";
import { CANVAS_VIEWPORT } from "constants/componentClassNameConstants";
import { klona as clone } from "klona/full";
import { klona as klonaFull } from "klona/full";
import { klona as klonaRegular } from "klona";
import { klona as klonaLite } from "klona/lite";
import { klona as klonaJson } from "klona/json";

import { startAndEndSpanForFn } from "UITelemetry/generateTraces";

export const snapToGrid = (
columnWidth: number,
Expand Down Expand Up @@ -818,6 +823,34 @@ export function isValidColor(color: string) {
return color?.includes("url") || validateColor(color) || isEmptyOrNill(color);
}

function klonaWithTelemetryWrapper<T>(
value: T,
codeSegment: string,
variant: string,
klonaFn: (input: T) => T,
): T {
return startAndEndSpanForFn(
"klona",
{
codeSegment,
variant,
},
() => klonaFn(value),
);
}
export function klonaFullWithTelemetry<T>(value: T, codeSegment: string): T {
return klonaWithTelemetryWrapper(value, codeSegment, "full", klonaFull);
}
export function klonaRegularWithTelemetry<T>(value: T, codeSegment: string): T {
return klonaWithTelemetryWrapper(value, codeSegment, "regular", klonaRegular);
}
export function klonaLiteWithTelemetry<T>(value: T, codeSegment: string): T {
return klonaWithTelemetryWrapper(value, codeSegment, "lite", klonaLite);
}
export function klonaJsonWithTelemetry<T>(value: T, codeSegment: string): T {
return klonaWithTelemetryWrapper(value, codeSegment, "json", klonaJson);
}

/*
* Function to merge property pane config of a widget
*
Expand All @@ -828,7 +861,10 @@ export const mergeWidgetConfig = (target: any, source: any) => {
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const sectionMap: Record<string, any> = {};
const mergedConfig = clone(target);
const mergedConfig = klonaFullWithTelemetry(
target,
"helpers.mergeWidgetConfig",
);

mergedConfig.forEach((section: { sectionName: string }) => {
sectionMap[section.sectionName] = section;
Expand Down
8 changes: 6 additions & 2 deletions app/client/src/widgets/ButtonGroupWidget/widget/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ import type {
AutocompletionDefinitions,
} from "WidgetProvider/constants";
import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants";
import { klona as clone } from "klona/full";
import { ResponsiveBehavior } from "layoutSystems/common/utils/constants";
import { BlueprintOperationTypes } from "WidgetProvider/constants";
import IconSVG from "../icon.svg";
import ThumbnailSVG from "../thumbnail.svg";
import { WIDGET_TAGS, layoutConfigurations } from "constants/WidgetConstants";
import { klonaFullWithTelemetry } from "utils/helpers";

class ButtonGroupWidget extends BaseWidget<
ButtonGroupWidgetProps,
Expand Down Expand Up @@ -133,7 +133,11 @@ class ButtonGroupWidget extends BaseWidget<
{
type: BlueprintOperationTypes.MODIFY_PROPS,
fn: (widget: WidgetProps & { children?: WidgetProps[] }) => {
const groupButtons = clone(widget.groupButtons);
const groupButtons = klonaFullWithTelemetry(
widget.groupButtons,
"ButtonGroupWidget.groupButtons",
);

// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const dynamicBindingPathList: any[] = get(
Expand Down
Loading

0 comments on commit 7cdee52

Please sign in to comment.