feat: track response time of invoke and sendSync methods on ipcRenderer #275
feat: track response time of invoke and sendSync methods on ipcRenderer #275samuelmaddock merged 13 commits intonextfrom
invoke and sendSync methods on ipcRenderer #275Conversation
c8f1372 to
7cb718d
Compare
7cb718d to
22a4d7c
Compare
22a4d7c to
f47bca4
Compare
| const cleanedListener = async (event: Electron.IpcMainInvokeEvent, ...args: any[]) => { | ||
| const newArgs = getArgsFromPayload(args); | ||
| const result = await listener(event, ...newArgs); | ||
| return result; |
There was a problem hiding this comment.
It could be interesting to track the result as well so it can be later inspected. Not blocking for this PR.
There was a problem hiding this comment.
Added it to the GSoC issue. I can probably do it in a follow up PR.
src/index.ts
Outdated
| const getArgsFromPayload = (payload: any[]): any[] => { | ||
| if (payload[0] && typeof payload[0] === 'object' && payload[0].__uuid__devtron) { | ||
| // If the first argument is an object with __uuid__devtron, return its args property | ||
| return payload[0].args || []; | ||
| } | ||
| // Otherwise, return the payload as is | ||
| return payload; | ||
| }; |
There was a problem hiding this comment.
nit: it would be nice if we could extend/reuse this helper function above in trackIpcEvent
There was a problem hiding this comment.
Fixed: 18adb6d
Lmk if it still needs changes.
src/index.ts
Outdated
| } | ||
|
|
||
| function patchIpcMain() { | ||
| const listenerMap = new Map<string, Map<any, any>>(); // channel -> (originalListener -> tracked/cleaned Listener) |
There was a problem hiding this comment.
Appreciate the comment here!
nit: It might be nice to create named types to make this code more self documenting:
| const listenerMap = new Map<string, Map<any, any>>(); // channel -> (originalListener -> tracked/cleaned Listener) | |
| // top level | |
| type Channel = string | |
| type Listener = any | |
| ... | |
| const listenerMap = new Map<Channel, Map<Listener, Listener>>(); // channel -> (originalListener -> tracked/cleaned Listener) |
There was a problem hiding this comment.
[non-blocking] Nice, though it looks like (event: Electron.IpcMainEvent, ...args: any[]) => void, might be a better type for Listener? The best way to make these named types useful is to use them across the file, so I'd also encourage you to update the typings of channel and listener beyond just listenerMap
There was a problem hiding this comment.
[non-blocking] Similar to data, types benefit from having a single source of truth. It's a good idea to define types like this at the highest relevant level in the repo so that they can be reused across all files where the type is relevant. It might be good to declare the Channel type in src/types and use it in IpcEventData.channel. Since this file is the only (I think) that deals with listeners, it makes sense to just declare the listener type in this file for now
There was a problem hiding this comment.
Tried fixing it here: 7d5813e
Batched some other type-safety related changes into this commit as well.
If anything isn’t up to the mark, I’ll be happy to make the necessary changes.
| const updated = [...prev, event].slice(-MAX_EVENTS_TO_DISPLAY); | ||
| // If the event with the same UUID already exists, we update it | ||
| if (event.uuid && uuidMapRef.current.has(event.uuid)) { | ||
| const index = uuidMapRef.current.get(event.uuid)!; | ||
| if (index < prev.length) { | ||
| // update the old event to include a link to the new event | ||
| const oldEvent = updated[index]; | ||
| oldEvent.gotoSerialNumber = event.serialNumber; | ||
| updated[index] = oldEvent; | ||
| // add a link to the old event in the new event | ||
| updated[updated.length - 1] = { ...event, gotoSerialNumber: index + 1 }; | ||
| } | ||
| uuidMapRef.current.delete(event.uuid); | ||
| } else if (event.uuid) { | ||
| // If a UUID is encountered for the first time, we store its index | ||
| uuidMapRef.current.set(event.uuid, updated.length - 1); |
There was a problem hiding this comment.
I think you'll run into a couple of issues when the events array is at MAX_EVENTS_TO_DISPLAY
- uuidMapRef indices might be stale and point to shifted events
- new entries to uuidMapRef will all have the same index value since updated.length is now constant
|
Updated the NPM Package to 0.0.0-development.13, which includes all commits up to this point. While testing the npm package with Electron Fiddle, I noticed that the line |
| type UUID = string; | ||
| type SerialNumber = number; |
There was a problem hiding this comment.
[non-blocking] Move these up to the top-level shared types file to tie these types with the IPCEventData shape more explicitly
| type UUID = string; | |
| type SerialNumber = number; | |
| // in src/types/shared.ts | |
| export type UUID = string; | |
| export type SerialNumber = number; | |
| ... | |
| export interface IpcEventData { | |
| .... | |
| uuid?: UUID, | |
| } | |
| ... | |
| export interface IpcEventDataIndexed extends IpcEventData { | |
| serialNumber: SerialNumber; | |
| gotoSerialNumber?: SerialNumber; // For navigating to a specific event in the grid | |
| } |
| if (lockToBottomRef.current) { | ||
| requestAnimationFrame(() => { | ||
| requestAnimationFrame(() => { | ||
| gridRef.current?.api.ensureIndexVisible(updated.length - 1, 'bottom'); | ||
| scrollToRow(updated.length - 1, 'bottom'); |
There was a problem hiding this comment.
[non-blocking] Calling side-effects from within a React useState setter's callback is nonstandard. For requestAnimationFrame, consider using useLayoutEffect.
yangannyx
left a comment
There was a problem hiding this comment.
PR LGTM from a functionality standpoint! Left some nonblocking suggestions around better type reusability, but don't want to bike shed on it
9656a9b to
c2fabdf
Compare
|
Rebased onto the Note: This rebase rewrote commit hashes, so all commits show up as new. In reality, the only change since the last review is resolving merge conflicts in |
Co-authored-by: Sam Maddock <samuel.maddock@gmail.com>
Co-authored-by: Sam Maddock <samuel.maddock@gmail.com>
c2fabdf to
22927f9
Compare
Rebased and fixed merge conflicts again. |

Description of Change
ipcRenderer.invokeandipcRenderer.sendSyncto receive responses back from the main process..on,.off,.once,.addListener,.removeListener,.removeAllListeners,.handle,.handleOnce, and.removeHandlermethods ofipcMainto allow Devtron to track response times and detect when handlers or listeners are removed fromipcMain.Tracking
.sendSyncand.invokeresponse times:Linked Eventfield in the Details Panel will redirect the user to the linked event..sendSyncand.invokerequests. This UUID is then received by the main process, where it is extracted from the arguments. As a result, both processes use the same UUID to track the events. Events with matching UUIDs are then linked together in the frontend.Tracking
.onceand.handleOnce:ipcMain.onceis tracked as two separate events:ipcMain.removeListenerandipcMain.on.ipcMain.handleOnceis tracked as two separate events:ipcMain.removeHandlerandipcMain.handle.