From 7f22e1c34f0154d5a6bf446246ae8ab5ead21098 Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Wed, 10 Jul 2024 17:47:41 +0300 Subject: [PATCH 1/3] CW-performance-monitoring Added firebase performance monitoring --- src/pages/OldCommon/hooks/useCommonMembers.ts | 6 ++++++ .../hooks/useCases/useDiscussionMessagesById.ts | 11 ++++++++++- src/shared/utils/firebase.tsx | 4 +++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/pages/OldCommon/hooks/useCommonMembers.ts b/src/pages/OldCommon/hooks/useCommonMembers.ts index 34aa60f0e..3f16ad080 100644 --- a/src/pages/OldCommon/hooks/useCommonMembers.ts +++ b/src/pages/OldCommon/hooks/useCommonMembers.ts @@ -1,5 +1,6 @@ import { useCallback, useRef, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; +import { trace } from "firebase/performance"; import { CommonService, Logger, UserService } from "@/services"; import { store } from "@/shared/appConfig"; import { LoadingState } from "@/shared/interfaces"; @@ -10,6 +11,7 @@ import { selectUserStates, } from "@/store/states"; import { useDeepCompareEffect } from "react-use"; +import { perf } from "@/shared/utils/firebase"; interface Options { commonId?: string; @@ -111,6 +113,9 @@ export const useCommonMembers = ({ commonId }: Options): Return => { (async () => { try { + const useCommonMembersTrace = trace(perf, 'useCommonMembers'); + useCommonMembersTrace.start(); + const cachedUserStates = selectUserStates()(store.getState()); const hasUsersFromCache = commonMembers.some( ({ userId }) => cachedUserStates[userId]?.data, @@ -183,6 +188,7 @@ export const useCommonMembers = ({ commonId }: Options): Return => { }; }); dispatch(cacheActions.updateUserStates(fetchedUsers)); + useCommonMembersTrace.stop(); } catch (err) { Logger.error(err); setState((prevState) => ({ diff --git a/src/shared/hooks/useCases/useDiscussionMessagesById.ts b/src/shared/hooks/useCases/useDiscussionMessagesById.ts index a2921fb4a..ca0348c33 100644 --- a/src/shared/hooks/useCases/useDiscussionMessagesById.ts +++ b/src/shared/hooks/useCases/useDiscussionMessagesById.ts @@ -1,6 +1,7 @@ import { useState, useCallback } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useDeepCompareEffect, useUpdateEffect } from "react-use"; +import { trace } from "firebase/performance"; import { DiscussionMessageService, MESSAGES_NUMBER_IN_BATCH, @@ -20,7 +21,7 @@ import { User, } from "@/shared/models"; import { InternalLinkData } from "@/shared/utils"; -import firebase from "@/shared/utils/firebase"; +import firebase, { perf } from "@/shared/utils/firebase"; import { cacheActions, selectDiscussionMessagesStateByDiscussionId, @@ -153,6 +154,9 @@ export const useDiscussionMessagesById = ({ const fetchRepliedMessages = useCallback( async (messageId: string, endDate: Date): Promise => { + const fetchRepliedMessagesTrace = trace(perf, 'fetchRepliedMessagesTrace'); + fetchRepliedMessagesTrace.start(); + if (state.data?.find((item) => item.id === messageId)) { return Promise.resolve(); } @@ -206,6 +210,7 @@ export const useDiscussionMessagesById = ({ updatedDiscussionMessages: discussionsWithText, }), ); + fetchRepliedMessagesTrace.stop(); }, [ state.data, @@ -239,6 +244,9 @@ export const useDiscussionMessagesById = ({ } try { + const fetchDiscussionMessagesTrace = trace(perf, 'fetchDiscussionMessages'); + fetchDiscussionMessagesTrace.start(); + DiscussionMessageService.subscribeToDiscussionMessagesByDiscussionId( discussionId, lastVisible && lastVisible[discussionId], @@ -308,6 +316,7 @@ export const useDiscussionMessagesById = ({ setIsBatchLoading(false); }, ); + fetchDiscussionMessagesTrace.stop(); } catch(err) { setIsBatchLoading(false); } diff --git a/src/shared/utils/firebase.tsx b/src/shared/utils/firebase.tsx index b652bbd95..5ab2f677a 100644 --- a/src/shared/utils/firebase.tsx +++ b/src/shared/utils/firebase.tsx @@ -3,6 +3,7 @@ import "firebase/compat/auth"; import "firebase/compat/firestore"; import "firebase/compat/performance"; import "firebase/compat/storage"; +import { getPerformance } from "firebase/performance"; import { local } from "@/config"; import { Environment, REACT_APP_ENV } from "@/shared/constants"; import config from "../../config"; @@ -11,7 +12,7 @@ interface FirebaseError extends Error { code: string; } -firebase.initializeApp(config.firebase); +const app = firebase.initializeApp(config.firebase); if (REACT_APP_ENV === Environment.Local) { firebase.auth().useEmulator(local.firebase.authDomain); @@ -33,6 +34,7 @@ if (REACT_APP_ENV === Environment.Local) { }); } +export const perf = getPerformance(app); // firebase.firestore.setLogLevel("debug"); export const isFirebaseError = (error: any): error is FirebaseError => From 454eb11b3c0c0b24701d88ed7d85d2a95997889b Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Wed, 10 Jul 2024 18:31:15 +0300 Subject: [PATCH 2/3] CW-performance-monitoring Fix tests --- package.json | 1 + src/shared/utils/firebase.tsx | 14 +++++++++++++- src/shared/utils/tests/mockFetch.ts | 3 +++ yarn.lock | 27 +++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/shared/utils/tests/mockFetch.ts diff --git a/package.json b/package.json index bd0a4db27..1553bfedc 100644 --- a/package.json +++ b/package.json @@ -175,6 +175,7 @@ "identity-obj-proxy": "^3.0.0", "jest": "^27.5.1", "jest-extended": "^2.0.0", + "jest-fetch-mock": "^3.0.3", "lint-staged": ">=10", "prettier": "^2.1.1", "prop-types": "^15.8.1", diff --git a/src/shared/utils/firebase.tsx b/src/shared/utils/firebase.tsx index 5ab2f677a..3db4544c9 100644 --- a/src/shared/utils/firebase.tsx +++ b/src/shared/utils/firebase.tsx @@ -34,7 +34,19 @@ if (REACT_APP_ENV === Environment.Local) { }); } -export const perf = getPerformance(app); +let perf; +if (typeof window !== "undefined" && typeof window.fetch !== "undefined") { + perf = getPerformance(app); +} else { + perf = { + trace: () => ({ + start: () => {}, + stop: () => {}, + }), + }; +} + +export { perf }; // firebase.firestore.setLogLevel("debug"); export const isFirebaseError = (error: any): error is FirebaseError => diff --git a/src/shared/utils/tests/mockFetch.ts b/src/shared/utils/tests/mockFetch.ts new file mode 100644 index 000000000..c68b24a4d --- /dev/null +++ b/src/shared/utils/tests/mockFetch.ts @@ -0,0 +1,3 @@ +import fetchMock from 'jest-fetch-mock'; + +fetchMock.enableMocks(); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 22f3f316e..14ceec5b0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8608,6 +8608,13 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== +cross-fetch@^3.0.4: + version "3.1.8" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.8.tgz#0327eba65fd68a7d119f8fb2bf9334a1a7956f82" + integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg== + dependencies: + node-fetch "^2.6.12" + cross-spawn@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" @@ -12782,6 +12789,14 @@ jest-extended@^2.0.0: jest-diff "^27.2.5" jest-get-type "^27.0.6" +jest-fetch-mock@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz#31749c456ae27b8919d69824f1c2bd85fe0a1f3b" + integrity sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw== + dependencies: + cross-fetch "^3.0.4" + promise-polyfill "^8.1.3" + jest-get-type@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" @@ -14718,6 +14733,13 @@ node-fetch@^2, node-fetch@^2.1.2, node-fetch@^2.6.1, node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" +node-fetch@^2.6.12: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + node-forge@^1: version "1.3.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" @@ -16398,6 +16420,11 @@ promise-inflight@^1.0.1: resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== +promise-polyfill@^8.1.3: + version "8.3.0" + resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.3.0.tgz#9284810268138d103807b11f4e23d5e945a4db63" + integrity sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg== + promise.allsettled@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/promise.allsettled/-/promise.allsettled-1.0.5.tgz#2443f3d4b2aa8dfa560f6ac2aa6c4ea999d75f53" From 5ac183a3670656dc1f8579a587226d98527f0b75 Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Thu, 11 Jul 2024 12:08:17 +0300 Subject: [PATCH 3/3] CW-performance-monitoring added try/catch --- .../useCases/useDiscussionMessagesById.ts | 106 +++++++++--------- 1 file changed, 55 insertions(+), 51 deletions(-) diff --git a/src/shared/hooks/useCases/useDiscussionMessagesById.ts b/src/shared/hooks/useCases/useDiscussionMessagesById.ts index ca0348c33..6d03f2126 100644 --- a/src/shared/hooks/useCases/useDiscussionMessagesById.ts +++ b/src/shared/hooks/useCases/useDiscussionMessagesById.ts @@ -155,62 +155,66 @@ export const useDiscussionMessagesById = ({ const fetchRepliedMessages = useCallback( async (messageId: string, endDate: Date): Promise => { const fetchRepliedMessagesTrace = trace(perf, 'fetchRepliedMessagesTrace'); - fetchRepliedMessagesTrace.start(); + try { + fetchRepliedMessagesTrace.start(); - if (state.data?.find((item) => item.id === messageId)) { - return Promise.resolve(); - } + if (state.data?.find((item) => item.id === messageId)) { + return Promise.resolve(); + } - const { - updatedDiscussionMessages, - removedDiscussionMessages, - lastVisibleSnapshot, - } = await DiscussionMessageService.getDiscussionMessagesByEndDate( - discussionId, - lastVisible && lastVisible[discussionId], - endDate, - ); + const { + updatedDiscussionMessages, + removedDiscussionMessages, + lastVisibleSnapshot, + } = await DiscussionMessageService.getDiscussionMessagesByEndDate( + discussionId, + lastVisible && lastVisible[discussionId], + endDate, + ); - setLastVisible((prevVisible) => ({ - ...prevVisible, - [discussionId]: lastVisibleSnapshot, - })); - const discussionsWithText = await Promise.all( - updatedDiscussionMessages.map(async (discussionMessage) => { - const isUserDiscussionMessage = - checkIsUserDiscussionMessage(discussionMessage); - const isSystemMessage = - checkIsSystemDiscussionMessage(discussionMessage); + setLastVisible((prevVisible) => ({ + ...prevVisible, + [discussionId]: lastVisibleSnapshot, + })); + const discussionsWithText = await Promise.all( + updatedDiscussionMessages.map(async (discussionMessage) => { + const isUserDiscussionMessage = + checkIsUserDiscussionMessage(discussionMessage); + const isSystemMessage = + checkIsSystemDiscussionMessage(discussionMessage); - const parsedText = await getTextFromTextEditorString({ - userId, - ownerId: isUserDiscussionMessage ? discussionMessage.ownerId : null, - textEditorString: discussionMessage.text, - users, - commonId: discussionMessage.commonId, - systemMessage: isSystemMessage ? discussionMessage : undefined, - getCommonPagePath, - getCommonPageAboutTabPath, - directParent, - onUserClick, - onFeedItemClick, - onInternalLinkClick, - }); + const parsedText = await getTextFromTextEditorString({ + userId, + ownerId: isUserDiscussionMessage ? discussionMessage.ownerId : null, + textEditorString: discussionMessage.text, + users, + commonId: discussionMessage.commonId, + systemMessage: isSystemMessage ? discussionMessage : undefined, + getCommonPagePath, + getCommonPageAboutTabPath, + directParent, + onUserClick, + onFeedItemClick, + onInternalLinkClick, + }); - return { - ...discussionMessage, - parsedText, - }; - }), - ); - dispatch( - cacheActions.updateDiscussionMessagesStateByDiscussionId({ - discussionId, - removedDiscussionMessages, - updatedDiscussionMessages: discussionsWithText, - }), - ); - fetchRepliedMessagesTrace.stop(); + return { + ...discussionMessage, + parsedText, + }; + }), + ); + dispatch( + cacheActions.updateDiscussionMessagesStateByDiscussionId({ + discussionId, + removedDiscussionMessages, + updatedDiscussionMessages: discussionsWithText, + }), + ); + fetchRepliedMessagesTrace.stop(); + } catch(err) { + fetchRepliedMessagesTrace.stop(); + } }, [ state.data,