feat: Improve startup time by parallelizing module and link loading (#12731)
This commit is contained in:
1 parent
3b1dc0ced0
commit
bff5c00777
3 files changed
+199
-188
No files matched your search
@@ -116,7 +116,7 @@ export const declineOrderTransferRequestWorkflow = createWorkflow(
|
|||||||
): WorkflowData<void> {
|
): WorkflowData<void> {
|
||||||
const orderQuery = useQueryGraphStep({
|
const orderQuery = useQueryGraphStep({
|
||||||
entity: "order",
|
entity: "order",
|
||||||
fields: ["id", "version", "declineed_at"],
|
fields: ["id", "version", "declined_at"],
|
||||||
filters: { id: input.order_id },
|
filters: { id: input.order_id },
|
||||||
options: { throwIfKeyNotFound: true },
|
options: { throwIfKeyNotFound: true },
|
||||||
}).config({ name: "order-query" })
|
}).config({ name: "order-query" })
|
||||||
|
|||||||
@@ -395,104 +395,109 @@ class MedusaModule {
|
|||||||
|
|
||||||
const services: { [Key: string]: any }[] = []
|
const services: { [Key: string]: any }[] = []
|
||||||
|
|
||||||
for (const moduleOptions of modulesOptions) {
|
await promiseAll(
|
||||||
const {
|
modulesOptions.map(async (moduleOptions) => {
|
||||||
moduleKey,
|
const {
|
||||||
defaultPath,
|
moduleKey,
|
||||||
declaration,
|
defaultPath,
|
||||||
moduleExports,
|
declaration,
|
||||||
sharedContainer,
|
moduleExports,
|
||||||
moduleDefinition,
|
sharedContainer,
|
||||||
injectedDependencies,
|
moduleDefinition,
|
||||||
} = moduleOptions
|
injectedDependencies,
|
||||||
|
} = moduleOptions
|
||||||
|
|
||||||
const hashKey = simpleHash(
|
const hashKey = simpleHash(
|
||||||
stringifyCircular({ moduleKey, defaultPath, declaration })
|
stringifyCircular({ moduleKey, defaultPath, declaration })
|
||||||
)
|
)
|
||||||
|
|
||||||
let finishLoading: any
|
let finishLoading: any
|
||||||
let errorLoading: any
|
let errorLoading: any
|
||||||
|
|
||||||
const loadingPromise = new Promise((resolve, reject) => {
|
const loadingPromise = new Promise((resolve, reject) => {
|
||||||
finishLoading = resolve
|
finishLoading = resolve
|
||||||
errorLoading = reject
|
errorLoading = reject
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!loaderOnly && MedusaModule.instances_.has(hashKey)) {
|
if (!loaderOnly && MedusaModule.instances_.has(hashKey)) {
|
||||||
services.push(MedusaModule.instances_.get(hashKey)!)
|
services.push(MedusaModule.instances_.get(hashKey)!)
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!loaderOnly && MedusaModule.loading_.has(hashKey)) {
|
if (!loaderOnly && MedusaModule.loading_.has(hashKey)) {
|
||||||
services.push(await MedusaModule.loading_.get(hashKey))
|
services.push(await MedusaModule.loading_.get(hashKey))
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!loaderOnly) {
|
if (!loaderOnly) {
|
||||||
MedusaModule.loading_.set(hashKey, loadingPromise)
|
MedusaModule.loading_.set(hashKey, loadingPromise)
|
||||||
}
|
}
|
||||||
|
|
||||||
let modDeclaration =
|
let modDeclaration =
|
||||||
declaration ??
|
declaration ??
|
||||||
({} as InternalModuleDeclaration | ExternalModuleDeclaration)
|
({} as InternalModuleDeclaration | ExternalModuleDeclaration)
|
||||||
|
|
||||||
if (declaration?.scope !== MODULE_SCOPE.EXTERNAL) {
|
if (declaration?.scope !== MODULE_SCOPE.EXTERNAL) {
|
||||||
modDeclaration = {
|
modDeclaration = {
|
||||||
scope: declaration?.scope || MODULE_SCOPE.INTERNAL,
|
scope: declaration?.scope || MODULE_SCOPE.INTERNAL,
|
||||||
resolve: defaultPath,
|
resolve: defaultPath,
|
||||||
options: declaration?.options ?? declaration,
|
options: declaration?.options ?? declaration,
|
||||||
dependencies:
|
dependencies:
|
||||||
(declaration as InternalModuleDeclaration)?.dependencies ?? [],
|
(declaration as InternalModuleDeclaration)?.dependencies ?? [],
|
||||||
alias: declaration?.alias,
|
alias: declaration?.alias,
|
||||||
main: declaration?.main,
|
main: declaration?.main,
|
||||||
worker_mode: workerMode,
|
worker_mode: workerMode,
|
||||||
} as InternalModuleDeclaration
|
} as InternalModuleDeclaration
|
||||||
}
|
}
|
||||||
|
|
||||||
const container = sharedContainer ?? createMedusaContainer()
|
const container = sharedContainer ?? createMedusaContainer()
|
||||||
|
|
||||||
if (injectedDependencies) {
|
if (injectedDependencies) {
|
||||||
for (const service in injectedDependencies) {
|
for (const service in injectedDependencies) {
|
||||||
container.register(service, asValue(injectedDependencies[service]))
|
|
||||||
if (!container.hasRegistration(service)) {
|
|
||||||
container.register(service, asValue(injectedDependencies[service]))
|
container.register(service, asValue(injectedDependencies[service]))
|
||||||
|
if (!container.hasRegistration(service)) {
|
||||||
|
container.register(
|
||||||
|
service,
|
||||||
|
asValue(injectedDependencies[service])
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const moduleResolutions = registerMedusaModule(
|
const moduleResolutions = registerMedusaModule(
|
||||||
moduleKey,
|
moduleKey,
|
||||||
modDeclaration!,
|
modDeclaration!,
|
||||||
moduleExports,
|
moduleExports,
|
||||||
moduleDefinition
|
moduleDefinition
|
||||||
)
|
)
|
||||||
|
|
||||||
const logger_ =
|
const logger_ =
|
||||||
container.resolve(ContainerRegistrationKeys.LOGGER, {
|
container.resolve(ContainerRegistrationKeys.LOGGER, {
|
||||||
allowUnregistered: true,
|
allowUnregistered: true,
|
||||||
}) ?? logger
|
}) ?? logger
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await moduleLoader({
|
await moduleLoader({
|
||||||
container,
|
container,
|
||||||
|
moduleResolutions,
|
||||||
|
logger: logger_,
|
||||||
|
migrationOnly,
|
||||||
|
loaderOnly,
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
errorLoading(err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
|
||||||
|
loadedModules.push({
|
||||||
|
hashKey,
|
||||||
|
modDeclaration,
|
||||||
moduleResolutions,
|
moduleResolutions,
|
||||||
logger: logger_,
|
container,
|
||||||
migrationOnly,
|
finishLoading,
|
||||||
loaderOnly,
|
|
||||||
})
|
})
|
||||||
} catch (err) {
|
|
||||||
errorLoading(err)
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
|
|
||||||
loadedModules.push({
|
|
||||||
hashKey,
|
|
||||||
modDeclaration,
|
|
||||||
moduleResolutions,
|
|
||||||
container,
|
|
||||||
finishLoading,
|
|
||||||
})
|
})
|
||||||
}
|
)
|
||||||
|
|
||||||
if (loaderOnly) {
|
if (loaderOnly) {
|
||||||
loadedModules.forEach(({ finishLoading }) => finishLoading({}))
|
loadedModules.forEach(({ finishLoading }) => finishLoading({}))
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
composeTableName,
|
composeTableName,
|
||||||
ContainerRegistrationKeys,
|
ContainerRegistrationKeys,
|
||||||
Modules,
|
Modules,
|
||||||
|
promiseAll,
|
||||||
simpleHash,
|
simpleHash,
|
||||||
toPascalCase,
|
toPascalCase,
|
||||||
} from "@medusajs/framework/utils"
|
} from "@medusajs/framework/utils"
|
||||||
@@ -42,130 +43,135 @@ export const initialize = async (
|
|||||||
pluginLinksDefinitions ?? []
|
pluginLinksDefinitions ?? []
|
||||||
)
|
)
|
||||||
|
|
||||||
for (const linkDefinition of allLinksToLoad) {
|
await promiseAll(
|
||||||
const definition: ModuleJoinerConfig = JSON.parse(
|
allLinksToLoad.map(async (linkDefinition) => {
|
||||||
JSON.stringify(linkDefinition)
|
const definition: ModuleJoinerConfig = JSON.parse(
|
||||||
)
|
JSON.stringify(linkDefinition)
|
||||||
|
|
||||||
const [primary, foreign] = definition.relationships ?? []
|
|
||||||
|
|
||||||
if (definition.relationships?.length !== 2 && !definition.isReadOnlyLink) {
|
|
||||||
throw new Error(
|
|
||||||
`Link module ${definition.serviceName} can only link 2 modules.`
|
|
||||||
)
|
)
|
||||||
} else if (
|
|
||||||
foreign?.foreignKey?.split(",").length > 1 &&
|
|
||||||
!definition.isReadOnlyLink
|
|
||||||
) {
|
|
||||||
throw new Error(`Foreign key cannot be a composed key.`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(definition.extraDataFields)) {
|
const [primary, foreign] = definition.relationships ?? []
|
||||||
const extraDataFields = definition.extraDataFields
|
|
||||||
const definedDbFields = Object.keys(
|
|
||||||
definition.databaseConfig?.extraFields || {}
|
|
||||||
)
|
|
||||||
const difference = arrayDifference(extraDataFields, definedDbFields)
|
|
||||||
|
|
||||||
if (difference.length) {
|
if (
|
||||||
|
definition.relationships?.length !== 2 &&
|
||||||
|
!definition.isReadOnlyLink
|
||||||
|
) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`extraDataFields (fieldNames: ${difference.join(
|
`Link module ${definition.serviceName} can only link 2 modules.`
|
||||||
","
|
|
||||||
)}) need to be configured under databaseConfig (serviceName: ${
|
|
||||||
definition.serviceName
|
|
||||||
}).`
|
|
||||||
)
|
)
|
||||||
|
} else if (
|
||||||
|
foreign?.foreignKey?.split(",").length > 1 &&
|
||||||
|
!definition.isReadOnlyLink
|
||||||
|
) {
|
||||||
|
throw new Error(`Foreign key cannot be a composed key.`)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const serviceKey = !definition.isReadOnlyLink
|
if (Array.isArray(definition.extraDataFields)) {
|
||||||
? definition.serviceName ??
|
const extraDataFields = definition.extraDataFields
|
||||||
composeLinkName(
|
const definedDbFields = Object.keys(
|
||||||
primary.serviceName,
|
definition.databaseConfig?.extraFields || {}
|
||||||
primary.foreignKey,
|
|
||||||
foreign.serviceName,
|
|
||||||
foreign.foreignKey
|
|
||||||
)
|
)
|
||||||
: simpleHash(JSON.stringify(definition.extends))
|
const difference = arrayDifference(extraDataFields, definedDbFields)
|
||||||
|
|
||||||
if (modulesLoadedKeys.includes(serviceKey)) {
|
if (difference.length) {
|
||||||
continue
|
throw new Error(
|
||||||
} else if (serviceKey in allLinks) {
|
`extraDataFields (fieldNames: ${difference.join(
|
||||||
throw new Error(`Link module ${serviceKey} already defined.`)
|
","
|
||||||
}
|
)}) need to be configured under databaseConfig (serviceName: ${
|
||||||
|
definition.serviceName
|
||||||
if (definition.isReadOnlyLink) {
|
}).`
|
||||||
const extended: any[] = []
|
)
|
||||||
for (const extension of definition.extends ?? []) {
|
|
||||||
if (
|
|
||||||
modulesLoadedKeys.includes(extension.serviceName) &&
|
|
||||||
modulesLoadedKeys.includes(extension.relationship.serviceName)
|
|
||||||
) {
|
|
||||||
extended.push(extension)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
definition.extends = extended
|
const serviceKey = !definition.isReadOnlyLink
|
||||||
if (extended.length === 0) {
|
? definition.serviceName ??
|
||||||
continue
|
composeLinkName(
|
||||||
|
primary.serviceName,
|
||||||
|
primary.foreignKey,
|
||||||
|
foreign.serviceName,
|
||||||
|
foreign.foreignKey
|
||||||
|
)
|
||||||
|
: simpleHash(JSON.stringify(definition.extends))
|
||||||
|
|
||||||
|
if (modulesLoadedKeys.includes(serviceKey)) {
|
||||||
|
return
|
||||||
|
} else if (serviceKey in allLinks) {
|
||||||
|
throw new Error(`Link module ${serviceKey} already defined.`)
|
||||||
}
|
}
|
||||||
} else if (
|
|
||||||
!modulesLoadedKeys.includes(primary.serviceName) ||
|
|
||||||
!modulesLoadedKeys.includes(foreign.serviceName)
|
|
||||||
) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const logger =
|
if (definition.isReadOnlyLink) {
|
||||||
injectedDependencies?.[ContainerRegistrationKeys.LOGGER] ?? console
|
const extended: any[] = []
|
||||||
|
for (const extension of definition.extends ?? []) {
|
||||||
|
if (
|
||||||
|
modulesLoadedKeys.includes(extension.serviceName) &&
|
||||||
|
modulesLoadedKeys.includes(extension.relationship.serviceName)
|
||||||
|
) {
|
||||||
|
extended.push(extension)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
definition.schema = generateGraphQLSchema(definition, primary, foreign, {
|
definition.extends = extended
|
||||||
logger,
|
if (extended.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
!modulesLoadedKeys.includes(primary.serviceName) ||
|
||||||
|
!modulesLoadedKeys.includes(foreign.serviceName)
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const logger =
|
||||||
|
injectedDependencies?.[ContainerRegistrationKeys.LOGGER] ?? console
|
||||||
|
|
||||||
|
definition.schema = generateGraphQLSchema(definition, primary, foreign, {
|
||||||
|
logger,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!Array.isArray(definition.alias)) {
|
||||||
|
definition.alias = definition.alias ? [definition.alias] : []
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const alias of definition.alias) {
|
||||||
|
alias.args ??= {}
|
||||||
|
|
||||||
|
alias.entity = toPascalCase(
|
||||||
|
"Link_" +
|
||||||
|
(definition.databaseConfig?.tableName ??
|
||||||
|
composeTableName(
|
||||||
|
primary.serviceName,
|
||||||
|
primary.foreignKey,
|
||||||
|
foreign.serviceName,
|
||||||
|
foreign.foreignKey
|
||||||
|
))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const moduleDefinition = getLinkModuleDefinition(
|
||||||
|
definition,
|
||||||
|
primary,
|
||||||
|
foreign
|
||||||
|
) as ModuleExports
|
||||||
|
|
||||||
|
const linkModuleDefinition: LinkModuleDefinition = {
|
||||||
|
key: serviceKey,
|
||||||
|
label: serviceKey,
|
||||||
|
dependencies: [Modules.EVENT_BUS],
|
||||||
|
defaultModuleDeclaration: {
|
||||||
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const loaded = await MedusaModule.bootstrapLink({
|
||||||
|
definition: linkModuleDefinition,
|
||||||
|
declaration: options as InternalModuleDeclaration,
|
||||||
|
moduleExports: moduleDefinition,
|
||||||
|
injectedDependencies,
|
||||||
|
})
|
||||||
|
|
||||||
|
allLinks[serviceKey as string] = Object.values(loaded)[0]
|
||||||
})
|
})
|
||||||
|
)
|
||||||
if (!Array.isArray(definition.alias)) {
|
|
||||||
definition.alias = definition.alias ? [definition.alias] : []
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const alias of definition.alias) {
|
|
||||||
alias.args ??= {}
|
|
||||||
|
|
||||||
alias.entity = toPascalCase(
|
|
||||||
"Link_" +
|
|
||||||
(definition.databaseConfig?.tableName ??
|
|
||||||
composeTableName(
|
|
||||||
primary.serviceName,
|
|
||||||
primary.foreignKey,
|
|
||||||
foreign.serviceName,
|
|
||||||
foreign.foreignKey
|
|
||||||
))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const moduleDefinition = getLinkModuleDefinition(
|
|
||||||
definition,
|
|
||||||
primary,
|
|
||||||
foreign
|
|
||||||
) as ModuleExports
|
|
||||||
|
|
||||||
const linkModuleDefinition: LinkModuleDefinition = {
|
|
||||||
key: serviceKey,
|
|
||||||
label: serviceKey,
|
|
||||||
dependencies: [Modules.EVENT_BUS],
|
|
||||||
defaultModuleDeclaration: {
|
|
||||||
scope: MODULE_SCOPE.INTERNAL,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const loaded = await MedusaModule.bootstrapLink({
|
|
||||||
definition: linkModuleDefinition,
|
|
||||||
declaration: options as InternalModuleDeclaration,
|
|
||||||
moduleExports: moduleDefinition,
|
|
||||||
injectedDependencies,
|
|
||||||
})
|
|
||||||
|
|
||||||
allLinks[serviceKey as string] = Object.values(loaded)[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
return allLinks
|
return allLinks
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user