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

feat(core): allow using dataloader for references and collections #4321

Merged
merged 22 commits into from Sep 9, 2023

Conversation

darkbasic
Copy link
Collaborator

This is currently a very basic implementation so that we can start talking about where we want this headed. I left out the collection dataloader as well as options handling and kept it as simple as possible. The biggest priority right now is being able to build mikro-orm on ppc64le to actually test it: lerna/lerna#3676
I've tried to build just core but I had no luck as well:

niko@talos2 ~/devel/mikro-orm/packages/core $ yarn build
command not found: rimraf

@codecov
Copy link

codecov bot commented May 5, 2023

packages/core/src/EntityManager.ts Outdated Show resolved Hide resolved
packages/core/src/EntityManager.ts Outdated Show resolved Hide resolved
packages/core/src/EntityManager.ts Outdated Show resolved Hide resolved
packages/core/src/EntityManager.ts Outdated Show resolved Hide resolved
packages/core/src/EntityManager.ts Outdated Show resolved Hide resolved
packages/core/src/entity/Reference.ts Outdated Show resolved Hide resolved
packages/core/src/entity/Reference.ts Show resolved Hide resolved
@B4nan
Copy link
Member

B4nan commented May 5, 2023

The biggest priority right now is being able to build mikro-orm on ppc64le to actually test it: lerna/lerna#3676

Why would you need to build anything to test it? Write tests, they use ts-node, nothing needs to be build, or what am I missing? Tests will be needed either way, and you can run them in the CI so you are not blocked now.

Never heard of ppc64le, so idk how could I help you with that.

@darkbasic
Copy link
Collaborator Author

Why would you need to build anything to test it? Write tests, they use ts-node, nothing needs to be build, or what am I missing? Tests will be needed either way, and you can run them in the CI so you are not blocked now.

Because I already have tons of tests in my project which already makes use of an external library version of the dataloder.
They verify the result, test performance, etc so being able to start with them would be an headstart before I implement proper tests in mikro-orm itself.

@B4nan
Copy link
Member

B4nan commented May 5, 2023

Would turborepo be any better? We could switch to that for the task running, and keep lerna only for publishing.

@darkbasic
Copy link
Collaborator Author

Would turborepo be any better? We could switch to that for the task running, and keep lerna only for publishing.

It was, but unfortunately not anymore:
vercel/turbo#2616
vercel/turbo#1891 (comment)

They are planning to add ppc64le support back but I fear it's not going to happen anytime soon.

@darkbasic
Copy link
Collaborator Author

@B4nan if I could manage to find where that unsupported error comes from I could try to build my own ppc64 binary (or even emulate the x86 one with qemu-user) but unfortunately it's not so obvious:

niko@talos2 ~/devel $ git clone https://github.com/lerna/lerna.git
Cloning into 'lerna'...
remote: Enumerating objects: 43257, done.
remote: Counting objects: 100% (1411/1411), done.
remote: Compressing objects: 100% (814/814), done.
remote: Total 43257 (delta 744), reused 1131 (delta 563), pack-reused 41846
Receiving objects: 100% (43257/43257), 29.57 MiB | 4.05 MiB/s, done.
Resolving deltas: 100% (32033/32033), done.
niko@talos2 ~/devel $ cd lerna/
niko@talos2 ~/devel/lerna $ grep -ir "Unsupported architecture" .
niko@talos2 ~/devel/lerna $ grep -ir "architecture" .
./libs/commands/publish/src/lib/create-temp-licenses.ts:    // (give up on 32-bit architecture to avoid fs-extra warning)
./website/docs/features/project-graph.md:For Lerna (and Nx) to run tasks quickly and correctly, it creates a graph of the dependencies between all the projects in the repository.  Exploring this graph visually can be useful to understand why Lerna is behaving in a certain way and to get a high level view of your code architecture.
./website/src/components/about-lerna.tsx:    text: "Lerna comes with a powerful interactive workspace visualizer, helping you understand the architecture of your workspace.",
niko@talos2 ~/devel/lerna $ grep -ir "unsupported" .
./.git/hooks/fsmonitor-watchman.sample:	die "Unsupported query-fsmonitor hook version '$version'.\n" .
./.eslintrc.json:        "node/no-unsupported-features/es-syntax": "off",
./libs/e2e-utils/src/lib/fixture.ts:        throw new Error(`Unsupported package manager: ${this.packageManager}`);
./libs/e2e-utils/src/lib/fixture.ts:        throw new Error(`Unsupported package manager: ${this.packageManager}`);
./libs/e2e-utils/src/lib/fixture.ts:        throw new Error(`Unsupported package manager: ${this.packageManager}`);

@darkbasic
Copy link
Collaborator Author

Apparently the lerna error comes from nx: https://github.com/nrwl/nx/blob/master/packages/nx/src/native/index.js#L235

Do we actually use the nx stuff? I didn't even know nx was a thing in lerna...

@B4nan
Copy link
Member

B4nan commented May 5, 2023

Do we actually use the nx stuff? I didn't even know nx was a thing in lerna...

It is used as the default task runner now. Maybe this could be even replaced with yarn? Lerna is used basically just for task running in topological order, and for versioning/publishing (which happens in CI only).

@darkbasic
Copy link
Collaborator Author

It is used as the default task runner now. Maybe this could be even replaced with yarn?

The problem is that using yarn as a task runner it doesn't find the root dependencies from inside the workspaces:

niko@talos2 ~/devel/mikro-orm $ yarn workspace @mikro-orm/core run build
command not found: rimraf

@B4nan
Copy link
Member

B4nan commented May 5, 2023

I think there was some option to allow that?

-T,--top-level
Check the root workspace for scripts and/or binaries instead of the current one

Hmm, but that's for scripts, not dependencies...

@darkbasic
Copy link
Collaborator Author

Hmm, but that's for scripts, not dependencies...

Unfortunately yes, it would simply run the top level script.

@darkbasic
Copy link
Collaborator Author

yarnpkg/berry#5429

@merceyz
Copy link
Contributor

merceyz commented May 8, 2023

That's not what the documentation says.

Check the root workspace for scripts and/or binaries instead of the current one
https://yarnpkg.com/cli/run

Binaries in the documentation is what you're referring to as dependencies.

If you change

"clean": "rimraf ./dist",
to yarn run -T rimraf ./dist it will use rimraf from the root workspace.

@darkbasic
Copy link
Collaborator Author

darkbasic commented May 8, 2023

yarn run -T rimraf ./dist it will use rimraf from the root workspace.

@merceyz looks like it's working, but unfortunately I cannot manage to get the plugin-workspace-tools to run the build scripts in the correct dependency order.

Workspaces use a wildcard:

  "workspaces": [
    "packages/*"
  ],

yarn workspaces foreach run build runs them in alphabetical order I think, which leads to failure because it needs to build the packages it depends on first. Looking at the docs I've found the -t option (yarn workspaces foreach -t run build) but it doesn't lead to the correct order either. I've looked for issues in the bug tracker and I've found this: yarnpkg/yarn#7150

Unfortunately it's not clear whether it's for berry or yarn v1 and it looks like a won't fix basically. Am I missing something?

EDIT: @merceyz nevermind, it looks like the correct option was --topological-dev.

@darkbasic
Copy link
Collaborator Author

I've finally managed to build mikro-orm using yarn's own task runner and incorporate it into my project via portals. The only minor annoyance so far has been having to change @mikro-orm/core/typings imports to @mikro-orm/core/dist/typings, but portals are so convenient that it's a minor tradeoff I'm willing to pay. So far it passes all my tests, so tomorrow I'll add basic support for dataloading collections. From there we can add the remaining options and start adding some tests into mikro-orm itself.

@B4nan
Copy link
Member

B4nan commented May 9, 2023

change @mikro-orm/core/typings imports

You shouldn't be using any deep imports, what types are you missing that are not exported from the root of the package?

@darkbasic
Copy link
Collaborator Author

import type {
  EntityKey,
  EntityProps,
  ExpandProperty,
  ExpandScalar,
  FilterValue2,
  Loaded,
  Query,
  Scalar,
} from "@mikro-orm/core/dist/typings";
import type { EntityKey, IWrappedEntityInternal } from "@mikro-orm/core/dist/typings";

These are mostly for the find dataloader (a couple of them for the collection dataloader maybe).
I would have needed more (FilterValue, Query, FilterObject, Compute, ObjectQuery, FilterQuery...) but the find dataloader still doesn't cover 100% of the original types so I'm using some custom slightly smaller subtypes.

Apart from the dataloader I didn't have to use deep imports elsewhere in my app. I suggest to ignore them for the moment, I'll add a stub commit for the find dataloader and we decide if this is something which might be worth merging alongside with the ref and collection ones. If we decide to merge it we won't need these exports on the root package anymore, otherwise we can reason about exporting them.

@B4nan
Copy link
Member

B4nan commented May 9, 2023

Ok, we could also export those types under some namespace, e.g. InternalTypes.FilterValue, to allow working with them in such context.

@darkbasic
Copy link
Collaborator Author

darkbasic commented May 10, 2023

I've added a basic version of the Collections dataloader. It's not as straightforward as the Reference one, because for collections we have to filter the results to re-assign them to the original collections, but it shouldn't be too hard to understand either. Let me know if something is not clear. I've tested it against my own project test suite and it works well.

P.S.
Unfortunately I couldn't create an incremental commit because I had to rebase it to drop commits from #4331 (which is necessary for testing).
As soon as the PR to use yarn as a task runner gets merged it should be a little bit easier to follow the history.

@darkbasic darkbasic changed the title feat: initial ref dataloader implementation feat: initial reference and collection dataloader implementations May 10, 2023
@darkbasic
Copy link
Collaborator Author

darkbasic commented May 11, 2023

I've pushed the find dataloader as well. This one is quite a bit more complex because it basically tries to optimize any kind of possible query into the smallest possible number of queries. It doesn't always manage to be faster so it's not something that we want to enable by default for every query, but amazingly it manages to be faster in some real world graphql scenarios (at least in my application). Once you get the grasp out of it it's not that complex (it's the second time I've rewritten it and I've focused on keeping things simple while achieving worthwhile performance), but feel free to ask me all the questions you want including a full detailed explanation if needed. I wanted to make it capable of possibly covering the whole set of operators, but I don't want to do so at the further expense of performance and I'm basically gradually adding more whenever I find the right use case in my own application. I think that even having a slightly tighter scope compared to the normal find could still be fine because that covers 90% of the use cases and you can always create your own specialized dataloader for complex queries. I use to benchmark it case by case and enable it for the queries that I know will suffer a lot from the GraphQL inherent N+1 nature. Not sure if we want to merge it, it's definitely useful but might never reach the full operators coverage (and maybe that's not even necessary).

@darkbasic
Copy link
Collaborator Author

@B4nan the lock file has been regenerated and documentation has been created. I've also added a new global option to surgically enable each dataloader.

Copy link
Member

@B4nan B4nan left a comment

Choose a reason for hiding this comment

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

two final things before we merge

packages/core/src/utils/Utils.ts Outdated Show resolved Hide resolved
packages/core/src/utils/Configuration.ts Outdated Show resolved Hide resolved
packages/core/src/enums.ts Outdated Show resolved Hide resolved
@darkbasic
Copy link
Collaborator Author

@B4nan done, should be ready to merge.

docs/docs/dataloaders.md Outdated Show resolved Hide resolved
Copy link
Member

@B4nan B4nan left a comment

Choose a reason for hiding this comment

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

all right, thanks for holding on with me!

@B4nan B4nan changed the title feat: initial reference and collection dataloader implementations feat(core): allow using dataloader for references and collections Sep 9, 2023
@B4nan B4nan merged commit 53b9ca1 into mikro-orm:v6 Sep 9, 2023
9 checks passed
@B4nan B4nan mentioned this pull request Oct 21, 2023
22 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants