Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Prisma recordings on extended Prisma clients #140

Merged
merged 1 commit into from
Apr 23, 2024

Conversation

zermelo-wisen
Copy link
Collaborator

@zermelo-wisen zermelo-wisen commented Apr 7, 2024

Fixes #127

We do two types of Prisma recordings:

  1. SQL queries sent to the db as appmap sql query events.
  2. Prisma Client Queries (create, findUnique, findMany, ...) as appmap funcion call events.

This fixes two problems reported in #127, that seem to occur with extended Prisma clients.

  1. SQLs not recorded.
  2. Repeated Prisma Client Queries.

@zermelo-wisen zermelo-wisen force-pushed the fix/prisma-recordings-on-extended-clients branch 4 times, most recently from 2b8a072 to e684a54 Compare April 8, 2024 11:07
@zermelo-wisen zermelo-wisen marked this pull request as ready for review April 8, 2024 11:10

// Normally, we "Cannot assign to 'PrismaClient' because it is a read-only property."
// Fortunately with this hack it can be done.
const modHacked = mod as unknown as { PrismaClient: unknown };
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could use Object.defineProperty() instead?

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-argument
const result = Reflect.construct(target, argArray, newTarget);
if (!hookAttached) {
hookAttached = true;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't notice it before, but now I can see hookAttached is a global. I know it's an edge case, but it will break if there are multiple prisma clients (as can be configured with multiple prisma: entries in the config file). How about using a WeakSet instead to keep track of patched clients?

const params = ["data", "include", "where"].map((k) => {
return { type: "Identifier", name: k } as ESTree.Identifier;
});
const params = [{ type: "Identifier", name: "args" } as ESTree.Identifier];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why this change is necessary. Nb, you can use generate.identifier().

Copy link
Collaborator Author

@zermelo-wisen zermelo-wisen Apr 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There can be lots of options like data, select, include, where, orderBy, take, skip, distinct, cursor, relationLoadStrategy, etc. Having a parameter for each one seemed not to look good when exploring the AppMap in the viewer, since most of them are irrelevant in a specific call and end up showing as "undefined."

Making different subsets of parameters for each Prisma model query isn't practical either, because, for example, findMany can have around 10 options and many of them will again be "undefined" in a specific call.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, thanks.

if (requestParams.action && requestParams.model) {
prismaCall = recording.functionCall(
getFunctionInfo(requestParams.model, requestParams.action, moduleId),
requestParams.model,
[requestParams.args?.data, requestParams.args?.include, requestParams.args?.where],
[requestParams.args?.data, requestParams.args?.include, requestParams?.args?.where],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why only ? in one case? Surely either all can be undefined or none.

"value": "undefined",
"class": "String",
"name": "args",
"value": "'{"data":{"name":"Alice","email":"[email protected]"}}'",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this recorded as a string? Should probably be an object.

[requestParams.args?.data, requestParams.args?.include, requestParams?.args?.where],
);
// In extended prisma clients, we receive repeated calls
// with the same action, module and args. It can be seen
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect the problem might be that we're patching the same client multiple times. You can double check by looking at this, this.prototype and the proxy; if I'm right, the calls will have the same this and prototype but different proxies. In this case, perhaps you can track the created proxies using a WeakSet to ensure you're not repatching.

@zermelo-wisen zermelo-wisen force-pushed the fix/prisma-recordings-on-extended-clients branch 3 times, most recently from 1c2db9c to e1eb991 Compare April 18, 2024 10:01
@zermelo-wisen zermelo-wisen force-pushed the fix/prisma-recordings-on-extended-clients branch from e1eb991 to c16940c Compare April 23, 2024 06:35
@dividedmind dividedmind merged commit 2cee1a9 into main Apr 23, 2024
6 checks passed
@dividedmind dividedmind deleted the fix/prisma-recordings-on-extended-clients branch April 23, 2024 13:31
@appland-release
Copy link

🎉 This PR is included in version 2.21.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

SQL with Prisma client are not being recorded by AppMap
3 participants