-
-
Notifications
You must be signed in to change notification settings - Fork 502
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
Synchronous Cold Start Optimization #4164
Comments
Thanks for the suggestions, I was thinking about having a sync way to init the ORM too, but it would be more about making the async stuff later, once some of the async methods are used. But since the time for breaking changes is just now, it's a good idea to see if we can do better. One thing I can say right ahead - the whole |
I am not sure how to handle the caching. I think I will just change the cache adapter interface to be sync and use sync FS method variants. In the end the use should be only during the init phase and should be skipped in production with the CLI command, so it shouldn't really matter. I will keep async API available for the result cache adapter and change only the one for metadata. Other than that, I have the initial draft almost done, but the production caching path will need more work. Maybe there could be a |
PR here #4166 |
@B4nan looks good, tracks with the tweaks I had tried locally in testing it out. i think |
And #4167 will add native support for the combined cache. This will also scratch the production deployment for ts-morph, finally a way to use it while not depending on the package in production builds. |
I guess I will create the database connection automatically on the first |
As opposed to the async `MikroORM.init` method, you can prefer to use synchronous variant `initSync`. This method has some limitations: - database connection will be established when you first interact with the database (or you can use `orm.connect()` explicitly) - no loading of the `config` file, `options` parameter is mandatory - no support for folder based discovery - no check for mismatched package versions Related: #4164
MikroORM now lets you generate production cache bundle into a single JSON file via CLI: ```bash npx mikro-orm cache:generate --combined ``` This will create `./temp/metadata.json` file which can be used together with `GeneratedCacheAdapter` in your production configuration: ```ts import { GeneratedCacheAdapter, MikroORM } from '@mikro-orm/core'; await MikroORM.init({ metadataCache: { enabled: true, adapter: GeneratedCacheAdapter, options: { data: require('./temp/metadata.json') }, }, // ... }); ``` This way you can keep the `@mikro-orm/reflection` package as a development dependency only, use the CLI to create the cache bundle, and depend only on that in your production build. > The cache bundle can be statically imported, which is handy in case you are using some bundler. Closes #4164
All right, both are now merged, let me know how it works. |
@B4nan trying to debug issue but it appears the auto connection isn't happening. to note, i use forking a lot:
|
@B4nan it appears the places you added mikro-orm/packages/knex/src/query/QueryBuilder.ts Lines 583 to 597 in d714ccc
since the |
Don't you have a better stack trace than just this? I think
I am able to make things fail if I use the QB as the first place after, but the stack trace is different:
Obviously, I can do this to make it fail your way, is that what you are doing somewhere?
Since the knex init is sync, we should just do it eagerly, could even happen inside the connection constructor probably. |
As opposed to the async `MikroORM.init` method, you can prefer to use synchronous variant `initSync`. This method has some limitations: - database connection will be established when you first interact with the database (or you can use `orm.connect()` explicitly) - no loading of the `config` file, `options` parameter is mandatory - no support for folder based discovery - no check for mismatched package versions Related: #4164
As opposed to the async `MikroORM.init` method, you can prefer to use synchronous variant `initSync`. This method has some limitations: - database connection will be established when you first interact with the database (or you can use `orm.connect()` explicitly) - no loading of the `config` file, `options` parameter is mandatory - no support for folder based discovery - no check for mismatched package versions Related: #4164
MikroORM now lets you generate production cache bundle into a single JSON file via CLI: ```bash npx mikro-orm cache:generate --combined ``` This will create `./temp/metadata.json` file which can be used together with `GeneratedCacheAdapter` in your production configuration: ```ts import { GeneratedCacheAdapter, MikroORM } from '@mikro-orm/core'; await MikroORM.init({ metadataCache: { enabled: true, adapter: GeneratedCacheAdapter, options: { data: require('./temp/metadata.json') }, }, // ... }); ``` This way you can keep the `@mikro-orm/reflection` package as a development dependency only, use the CLI to create the cache bundle, and depend only on that in your production build. > The cache bundle can be statically imported, which is handy in case you are using some bundler. Closes #4164
As opposed to the async `MikroORM.init` method, you can prefer to use synchronous variant `initSync`. This method has some limitations: - database connection will be established when you first interact with the database (or you can use `orm.connect()` explicitly) - no loading of the `config` file, `options` parameter is mandatory - no support for folder based discovery - no check for mismatched package versions Related: #4164
MikroORM now lets you generate production cache bundle into a single JSON file via CLI: ```bash npx mikro-orm cache:generate --combined ``` This will create `./temp/metadata.json` file which can be used together with `GeneratedCacheAdapter` in your production configuration: ```ts import { GeneratedCacheAdapter, MikroORM } from '@mikro-orm/core'; await MikroORM.init({ metadataCache: { enabled: true, adapter: GeneratedCacheAdapter, options: { data: require('./temp/metadata.json') }, }, // ... }); ``` This way you can keep the `@mikro-orm/reflection` package as a development dependency only, use the CLI to create the cache bundle, and depend only on that in your production build. > The cache bundle can be statically imported, which is handy in case you are using some bundler. Closes #4164
As opposed to the async `MikroORM.init` method, you can prefer to use synchronous variant `initSync`. This method has some limitations: - database connection will be established when you first interact with the database (or you can use `orm.connect()` explicitly) - no loading of the `config` file, `options` parameter is mandatory - no support for folder based discovery - no check for mismatched package versions Related: #4164
MikroORM now lets you generate production cache bundle into a single JSON file via CLI: ```bash npx mikro-orm cache:generate --combined ``` This will create `./temp/metadata.json` file which can be used together with `GeneratedCacheAdapter` in your production configuration: ```ts import { GeneratedCacheAdapter, MikroORM } from '@mikro-orm/core'; await MikroORM.init({ metadataCache: { enabled: true, adapter: GeneratedCacheAdapter, options: { data: require('./temp/metadata.json') }, }, // ... }); ``` This way you can keep the `@mikro-orm/reflection` package as a development dependency only, use the CLI to create the cache bundle, and depend only on that in your production build. > The cache bundle can be statically imported, which is handy in case you are using some bundler. Closes #4164
As opposed to the async `MikroORM.init` method, you can prefer to use synchronous variant `initSync`. This method has some limitations: - database connection will be established when you first interact with the database (or you can use `orm.connect()` explicitly) - no loading of the `config` file, `options` parameter is mandatory - no support for folder based discovery - no check for mismatched package versions Related: #4164
MikroORM now lets you generate production cache bundle into a single JSON file via CLI: ```bash npx mikro-orm cache:generate --combined ``` This will create `./temp/metadata.json` file which can be used together with `GeneratedCacheAdapter` in your production configuration: ```ts import { GeneratedCacheAdapter, MikroORM } from '@mikro-orm/core'; await MikroORM.init({ metadataCache: { enabled: true, adapter: GeneratedCacheAdapter, options: { data: require('./temp/metadata.json') }, }, // ... }); ``` This way you can keep the `@mikro-orm/reflection` package as a development dependency only, use the CLI to create the cache bundle, and depend only on that in your production build. > The cache bundle can be statically imported, which is handy in case you are using some bundler. Closes #4164
As opposed to the async `MikroORM.init` method, you can prefer to use synchronous variant `initSync`. This method has some limitations: - database connection will be established when you first interact with the database (or you can use `orm.connect()` explicitly) - no loading of the `config` file, `options` parameter is mandatory - no support for folder based discovery - no check for mismatched package versions Related: #4164
MikroORM now lets you generate production cache bundle into a single JSON file via CLI: ```bash npx mikro-orm cache:generate --combined ``` This will create `./temp/metadata.json` file which can be used together with `GeneratedCacheAdapter` in your production configuration: ```ts import { GeneratedCacheAdapter, MikroORM } from '@mikro-orm/core'; await MikroORM.init({ metadataCache: { enabled: true, adapter: GeneratedCacheAdapter, options: { data: require('./temp/metadata.json') }, }, // ... }); ``` This way you can keep the `@mikro-orm/reflection` package as a development dependency only, use the CLI to create the cache bundle, and depend only on that in your production build. > The cache bundle can be statically imported, which is handy in case you are using some bundler. Closes #4164
As opposed to the async `MikroORM.init` method, you can prefer to use synchronous variant `initSync`. This method has some limitations: - database connection will be established when you first interact with the database (or you can use `orm.connect()` explicitly) - no loading of the `config` file, `options` parameter is mandatory - no support for folder based discovery - no check for mismatched package versions Related: #4164
MikroORM now lets you generate production cache bundle into a single JSON file via CLI: ```bash npx mikro-orm cache:generate --combined ``` This will create `./temp/metadata.json` file which can be used together with `GeneratedCacheAdapter` in your production configuration: ```ts import { GeneratedCacheAdapter, MikroORM } from '@mikro-orm/core'; await MikroORM.init({ metadataCache: { enabled: true, adapter: GeneratedCacheAdapter, options: { data: require('./temp/metadata.json') }, }, // ... }); ``` This way you can keep the `@mikro-orm/reflection` package as a development dependency only, use the CLI to create the cache bundle, and depend only on that in your production build. > The cache bundle can be statically imported, which is handy in case you are using some bundler. Closes #4164
As opposed to the async `MikroORM.init` method, you can prefer to use synchronous variant `initSync`. This method has some limitations: - database connection will be established when you first interact with the database (or you can use `orm.connect()` explicitly) - no loading of the `config` file, `options` parameter is mandatory - no support for folder based discovery - no check for mismatched package versions Related: #4164
MikroORM now lets you generate production cache bundle into a single JSON file via CLI: ```bash npx mikro-orm cache:generate --combined ``` This will create `./temp/metadata.json` file which can be used together with `GeneratedCacheAdapter` in your production configuration: ```ts import { GeneratedCacheAdapter, MikroORM } from '@mikro-orm/core'; await MikroORM.init({ metadataCache: { enabled: true, adapter: GeneratedCacheAdapter, options: { data: require('./temp/metadata.json') }, }, // ... }); ``` This way you can keep the `@mikro-orm/reflection` package as a development dependency only, use the CLI to create the cache bundle, and depend only on that in your production build. > The cache bundle can be statically imported, which is handy in case you are using some bundler. Closes #4164
As opposed to the async `MikroORM.init` method, you can prefer to use synchronous variant `initSync`. This method has some limitations: - database connection will be established when you first interact with the database (or you can use `orm.connect()` explicitly) - no loading of the `config` file, `options` parameter is mandatory - no support for folder based discovery - no check for mismatched package versions Related: #4164
MikroORM now lets you generate production cache bundle into a single JSON file via CLI: ```bash npx mikro-orm cache:generate --combined ``` This will create `./temp/metadata.json` file which can be used together with `GeneratedCacheAdapter` in your production configuration: ```ts import { GeneratedCacheAdapter, MikroORM } from '@mikro-orm/core'; await MikroORM.init({ metadataCache: { enabled: true, adapter: GeneratedCacheAdapter, options: { data: require('./temp/metadata.json') }, }, // ... }); ``` This way you can keep the `@mikro-orm/reflection` package as a development dependency only, use the CLI to create the cache bundle, and depend only on that in your production build. > The cache bundle can be statically imported, which is handy in case you are using some bundler. Closes #4164
As opposed to the async `MikroORM.init` method, you can prefer to use synchronous variant `initSync`. This method has some limitations: - database connection will be established when you first interact with the database (or you can use `orm.connect()` explicitly) - no loading of the `config` file, `options` parameter is mandatory - no support for folder based discovery - no check for mismatched package versions Related: #4164
MikroORM now lets you generate production cache bundle into a single JSON file via CLI: ```bash npx mikro-orm cache:generate --combined ``` This will create `./temp/metadata.json` file which can be used together with `GeneratedCacheAdapter` in your production configuration: ```ts import { GeneratedCacheAdapter, MikroORM } from '@mikro-orm/core'; await MikroORM.init({ metadataCache: { enabled: true, adapter: GeneratedCacheAdapter, options: { data: require('./temp/metadata.json') }, }, // ... }); ``` This way you can keep the `@mikro-orm/reflection` package as a development dependency only, use the CLI to create the cache bundle, and depend only on that in your production build. > The cache bundle can be statically imported, which is handy in case you are using some bundler. Closes #4164
As opposed to the async `MikroORM.init` method, you can prefer to use synchronous variant `initSync`. This method has some limitations: - database connection will be established when you first interact with the database (or you can use `orm.connect()` explicitly) - no loading of the `config` file, `options` parameter is mandatory - no support for folder based discovery - no check for mismatched package versions Related: #4164
MikroORM now lets you generate production cache bundle into a single JSON file via CLI: ```bash npx mikro-orm cache:generate --combined ``` This will create `./temp/metadata.json` file which can be used together with `GeneratedCacheAdapter` in your production configuration: ```ts import { GeneratedCacheAdapter, MikroORM } from '@mikro-orm/core'; await MikroORM.init({ metadataCache: { enabled: true, adapter: GeneratedCacheAdapter, options: { data: require('./temp/metadata.json') }, }, // ... }); ``` This way you can keep the `@mikro-orm/reflection` package as a development dependency only, use the CLI to create the cache bundle, and depend only on that in your production build. > The cache bundle can be statically imported, which is handy in case you are using some bundler. Closes #4164
As opposed to the async `MikroORM.init` method, you can prefer to use synchronous variant `initSync`. This method has some limitations: - database connection will be established when you first interact with the database (or you can use `orm.connect()` explicitly) - no loading of the `config` file, `options` parameter is mandatory - no support for folder based discovery - no check for mismatched package versions Related: #4164
MikroORM now lets you generate production cache bundle into a single JSON file via CLI: ```bash npx mikro-orm cache:generate --combined ``` This will create `./temp/metadata.json` file which can be used together with `GeneratedCacheAdapter` in your production configuration: ```ts import { GeneratedCacheAdapter, MikroORM } from '@mikro-orm/core'; await MikroORM.init({ metadataCache: { enabled: true, adapter: GeneratedCacheAdapter, options: { data: require('./temp/metadata.json') }, }, // ... }); ``` This way you can keep the `@mikro-orm/reflection` package as a development dependency only, use the CLI to create the cache bundle, and depend only on that in your production build. > The cache bundle can be statically imported, which is handy in case you are using some bundler. Closes #4164
As opposed to the async `MikroORM.init` method, you can prefer to use synchronous variant `initSync`. This method has some limitations: - database connection will be established when you first interact with the database (or you can use `orm.connect()` explicitly) - no loading of the `config` file, `options` parameter is mandatory - no support for folder based discovery - no check for mismatched package versions Related: #4164
MikroORM now lets you generate production cache bundle into a single JSON file via CLI: ```bash npx mikro-orm cache:generate --combined ``` This will create `./temp/metadata.json` file which can be used together with `GeneratedCacheAdapter` in your production configuration: ```ts import { GeneratedCacheAdapter, MikroORM } from '@mikro-orm/core'; await MikroORM.init({ metadataCache: { enabled: true, adapter: GeneratedCacheAdapter, options: { data: require('./temp/metadata.json') }, }, // ... }); ``` This way you can keep the `@mikro-orm/reflection` package as a development dependency only, use the CLI to create the cache bundle, and depend only on that in your production build. > The cache bundle can be statically imported, which is handy in case you are using some bundler. Closes #4164
As opposed to the async `MikroORM.init` method, you can prefer to use synchronous variant `initSync`. This method has some limitations: - database connection will be established when you first interact with the database (or you can use `orm.connect()` explicitly) - no loading of the `config` file, `options` parameter is mandatory - no support for folder based discovery - no check for mismatched package versions Related: #4164
MikroORM now lets you generate production cache bundle into a single JSON file via CLI: ```bash npx mikro-orm cache:generate --combined ``` This will create `./temp/metadata.json` file which can be used together with `GeneratedCacheAdapter` in your production configuration: ```ts import { GeneratedCacheAdapter, MikroORM } from '@mikro-orm/core'; await MikroORM.init({ metadataCache: { enabled: true, adapter: GeneratedCacheAdapter, options: { data: require('./temp/metadata.json') }, }, // ... }); ``` This way you can keep the `@mikro-orm/reflection` package as a development dependency only, use the CLI to create the cache bundle, and depend only on that in your production build. > The cache bundle can be statically imported, which is handy in case you are using some bundler. Closes #4164
As opposed to the async `MikroORM.init` method, you can prefer to use synchronous variant `initSync`. This method has some limitations: - database connection will be established when you first interact with the database (or you can use `orm.connect()` explicitly) - no loading of the `config` file, `options` parameter is mandatory - no support for folder based discovery - no check for mismatched package versions Related: #4164
MikroORM now lets you generate production cache bundle into a single JSON file via CLI: ```bash npx mikro-orm cache:generate --combined ``` This will create `./temp/metadata.json` file which can be used together with `GeneratedCacheAdapter` in your production configuration: ```ts import { GeneratedCacheAdapter, MikroORM } from '@mikro-orm/core'; await MikroORM.init({ metadataCache: { enabled: true, adapter: GeneratedCacheAdapter, options: { data: require('./temp/metadata.json') }, }, // ... }); ``` This way you can keep the `@mikro-orm/reflection` package as a development dependency only, use the CLI to create the cache bundle, and depend only on that in your production build. > The cache bundle can be statically imported, which is handy in case you are using some bundler. Closes #4164
As opposed to the async `MikroORM.init` method, you can prefer to use synchronous variant `initSync`. This method has some limitations: - database connection will be established when you first interact with the database (or you can use `orm.connect()` explicitly) - no loading of the `config` file, `options` parameter is mandatory - no support for folder based discovery - no check for mismatched package versions Related: #4164
MikroORM now lets you generate production cache bundle into a single JSON file via CLI: ```bash npx mikro-orm cache:generate --combined ``` This will create `./temp/metadata.json` file which can be used together with `GeneratedCacheAdapter` in your production configuration: ```ts import { GeneratedCacheAdapter, MikroORM } from '@mikro-orm/core'; await MikroORM.init({ metadataCache: { enabled: true, adapter: GeneratedCacheAdapter, options: { data: require('./temp/metadata.json') }, }, // ... }); ``` This way you can keep the `@mikro-orm/reflection` package as a development dependency only, use the CLI to create the cache bundle, and depend only on that in your production build. > The cache bundle can be statically imported, which is handy in case you are using some bundler. Closes #4164
I'm making a big push to migrate my projects which include MikroORM over to serverless/edge platforms and the bootstrapping of the ORM is the only async dependency I have which causes a bit of cruft to manage global promises since async dependencies can only be initiated on the first request, then cached. I propose a modification to the internals of how entity discovery (and also metadata generation) works to make this process a little more ergonomic for non-monolithic deployments.
Bundler Friendly Metadata
As most serverless frameworks/platforms often include or abstract away the compilation process (as well as the resulting file structure) it is important that there is a blessed workflow for including all the runtime information Mikro needs via compilation vs. relying on init options that can be hard to trace down (eg: file system locations of compiled/source entities).
For my project I have a multistep process for handling metadata that eliminates that runtime headaches:
Use a watch command during development (or manual if user chooses) that simply executes
mikro-orm cache:generate
as documented to produce JSON for every discovered entity.Combine all JSON entities into a single file that can be imported (and inline'd during compilation) like so:
GeneratedMetadataCacheAdapter
that does not rely on promises and can do a simple map lookup when building the metadata storage:"Optionally Async" Metadata Discovery
At the moment, most of the behavior that happens in
MikroORM.init()
, I have no need for and my life in serverless-land could be much happier if I had a way to opt-out or invoke init in a try/fail capacity. Stepping through the code path here is what I gathered:MikroORM.init()
is effectively doing 3 things that can be "opt-in" by the user just invoking what they need on the ORM instance themselves and no changes toinit()
are required.That basically leaves the metadata discovery logic which can probably be tweaked a bit to support both synchronous and "opt-in" like behavior as well:
findEntities()
is pretty broad and setup in a way to handle every config/option permutation. In the case of using the generated metadata above (along with the cache provider), the only two functions in that call that are needed becomediscoverReferences()
anddiscoverMissingTargets()
(in my case, I pass an array of entity classes names to Mikro options).discoverEntity()
does not need to be async since the metadata adapter above is completely in-memory. given the adapter has a strong assumption of remote/async cache management i think a new code-path is probably more appropriate than trying to tweak the cache adapter design. Granted there isJavaScriptMetadataProvider
, but that still has some downstream work to turn it into a format that Mikro needs at runtime vs having an optimized metadata object generated ahead of time.processDiscoveredEntities()
the only reason this appears to be async is for enum processing, which if that logic is happening ahead of time and included in the compilation, then this isn't needed. (i don't actually use @enum and simply rely on string types and use typescript enforcement at compiled time: eg:@Property({type:string}) foo: Bar
Based on the above, I see potentially two ways of handling this:
discoverEntities()
wherethis.metadata = await
is replaced with the pre-generated/optimized JSON configuration that was done out-of-band and simply "sets" it:The text was updated successfully, but these errors were encountered: