Skip to content

Commit 13d7d15

Browse files
chore(modules-sdk): parallel migrations (medusajs#13898)
1 parent fffc1be commit 13d7d15

File tree

14 files changed

+124
-24
lines changed

14 files changed

+124
-24
lines changed

.changeset/young-eggs-rush.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"@medusajs/modules-sdk": patch
3+
"@medusajs/framework": patch
4+
"@medusajs/utils": patch
5+
"@medusajs/test-utils": patch
6+
"@medusajs/medusa": patch
7+
---
8+
9+
chore(modules-sdk): parallel migrations

packages/core/framework/src/migrations/migrator.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { MedusaContainer } from "@medusajs/types"
2-
import { Knex } from "../deps/mikro-orm-knex"
32
import { glob } from "glob"
43
import { join } from "path"
4+
import { Knex } from "../deps/mikro-orm-knex"
55
import { logger } from "../logger"
66
import { ContainerRegistrationKeys } from "../utils"
77

8-
export abstract class Migrator {
9-
protected abstract migration_table_name: string
8+
export class Migrator {
9+
protected migration_table_name: string
1010

1111
protected container: MedusaContainer
1212
protected pgConnection: Knex<any>
@@ -18,6 +18,7 @@ export abstract class Migrator {
1818
this.pgConnection = this.container.resolve(
1919
ContainerRegistrationKeys.PG_CONNECTION
2020
)
21+
this.migration_table_name = "mikro_orm_migrations"
2122
}
2223

2324
/**
@@ -158,7 +159,21 @@ export abstract class Migrator {
158159
return allScripts
159160
}
160161

161-
protected abstract createMigrationTable(): Promise<void>
162-
abstract run(...args: any[]): Promise<any>
163-
abstract getPendingMigrations(migrationPaths: string[]): Promise<string[]>
162+
protected async createMigrationTable(): Promise<void> {
163+
await this.pgConnection.raw(`
164+
CREATE TABLE IF NOT EXISTS ${this.migration_table_name} (
165+
id serial PRIMARY KEY,
166+
name varchar(255),
167+
executed_at timestamptz DEFAULT CURRENT_TIMESTAMP
168+
)
169+
`)
170+
}
171+
172+
run(...args: any[]): Promise<any> {
173+
throw new Error("Method not implemented")
174+
}
175+
176+
getPendingMigrations(migrationPaths: string[]): Promise<string[]> {
177+
throw new Error("Method not implemented")
178+
}
164179
}

packages/core/modules-sdk/src/medusa-app.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { asValue } from "@medusajs/deps/awilix"
12
import { RemoteFetchDataCallback } from "@medusajs/orchestration"
23
import {
34
ConfigModule,
@@ -21,6 +22,7 @@ import {
2122
createMedusaContainer,
2223
discoverFeatureFlagsFromDir,
2324
dynamicImport,
25+
executeWithConcurrency,
2426
FeatureFlag,
2527
GraphQLUtils,
2628
isObject,
@@ -33,7 +35,6 @@ import {
3335
promiseAll,
3436
registerFeatureFlag,
3537
} from "@medusajs/utils"
36-
import { asValue } from "@medusajs/deps/awilix"
3738
import { Link } from "./link"
3839
import {
3940
MedusaModule,
@@ -501,12 +502,14 @@ async function MedusaApp_({
501502
modulesNames: string[]
502503
action?: "run" | "revert" | "generate"
503504
}) => {
504-
const moduleResolutions = modulesNames.map((moduleName) => {
505-
return {
506-
moduleName,
507-
resolution: MedusaModule.getModuleResolutions(moduleName),
505+
const moduleResolutions = Array.from(new Set(modulesNames)).map(
506+
(moduleName) => {
507+
return {
508+
moduleName,
509+
resolution: MedusaModule.getModuleResolutions(moduleName),
510+
}
508511
}
509-
})
512+
)
510513

511514
const missingModules = moduleResolutions
512515
.filter(({ resolution }) => !resolution)
@@ -524,7 +527,7 @@ async function MedusaApp_({
524527
throw error
525528
}
526529

527-
for (const { resolution: moduleResolution } of moduleResolutions) {
530+
const run = async ({ resolution: moduleResolution }) => {
528531
if (
529532
!moduleResolution.options?.database &&
530533
moduleResolution.moduleDeclaration?.scope === MODULE_SCOPE.INTERNAL
@@ -554,6 +557,11 @@ async function MedusaApp_({
554557
await MedusaModule.migrateGenerate(migrationOptions)
555558
}
556559
}
560+
561+
await executeWithConcurrency(
562+
moduleResolutions.map((a) => () => run(a)),
563+
8 // parallel migrations
564+
)
557565
}
558566

559567
const runMigrations: RunMigrationFn = async (): Promise<void> => {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* Execute functions with a concurrency limit
3+
* @param functions Array of functions to execute in parallel
4+
* @param concurrency Maximum number of concurrent executions
5+
*/
6+
export async function executeWithConcurrency<T>(
7+
functions: (() => Promise<T>)[],
8+
concurrency: number
9+
): Promise<PromiseSettledResult<Awaited<T>>[]> {
10+
const results: PromiseSettledResult<Awaited<T>>[] = new Array(
11+
functions.length
12+
)
13+
let currentIndex = 0
14+
15+
const executeNext = async (): Promise<void> => {
16+
while (currentIndex < functions.length) {
17+
const index = currentIndex++
18+
const result = await Promise.allSettled([functions[index]()])
19+
results[index] = result[0]
20+
}
21+
}
22+
23+
const workers: Promise<void>[] = []
24+
for (let i = 0; i < concurrency; i++) {
25+
workers.push(executeNext())
26+
}
27+
28+
await Promise.all(workers)
29+
30+
return results
31+
}

packages/core/utils/src/common/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export * from "./define-file-config"
1919
export * from "./dynamic-import"
2020
export * from "./env-editor"
2121
export * from "./errors"
22+
export * from "./execute-with-concurrency"
2223
export * from "./file-system"
2324
export * from "./filter-object-by-keys"
2425
export * from "./filter-operator-map"

packages/medusa-test-utils/src/medusa-test-runner.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { asValue } from "@medusajs/framework/awilix"
12
import { logger } from "@medusajs/framework/logger"
3+
import { Migrator } from "@medusajs/framework/migrations"
24
import { MedusaAppOutput } from "@medusajs/framework/modules-sdk"
35
import { MedusaContainer } from "@medusajs/framework/types"
46
import {
@@ -7,7 +9,6 @@ import {
79
getResolvedPlugins,
810
mergePluginModules,
911
} from "@medusajs/framework/utils"
10-
import { asValue } from "@medusajs/framework/awilix"
1112
import { dbTestUtilFactory, getDatabaseURL } from "./database"
1213
import {
1314
applyEnvVarsToProcess,
@@ -178,6 +179,9 @@ class MedusaTestRunner {
178179

179180
await this.initializeDatabase()
180181

182+
const migrator = new Migrator({ container })
183+
await migrator.ensureMigrationsTable()
184+
181185
logger.info(
182186
`Migrating database with core migrations and links ${this.dbName}`
183187
)

packages/medusa/src/commands/db/generate.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { MedusaAppLoader } from "@medusajs/framework"
1+
import { MedusaAppLoader, Migrator } from "@medusajs/framework"
22
import { LinkLoader } from "@medusajs/framework/links"
33
import {
44
ContainerRegistrationKeys,
@@ -41,6 +41,9 @@ const main = async function ({ directory, modules }) {
4141
*/
4242
logger.info("Generating migrations...")
4343

44+
const migrator = new Migrator({ container })
45+
await migrator.ensureMigrationsTable()
46+
4447
await medusaAppLoader.runModulesMigrations({
4548
moduleNames: modules,
4649
action: "generate",

packages/medusa/src/commands/db/migrate.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { MEDUSA_CLI_PATH, MedusaAppLoader } from "@medusajs/framework"
1+
import { MEDUSA_CLI_PATH, MedusaAppLoader, Migrator } from "@medusajs/framework"
22
import { LinkLoader } from "@medusajs/framework/links"
33
import {
44
ContainerRegistrationKeys,
@@ -60,6 +60,10 @@ export async function migrate({
6060
* Run migrations
6161
*/
6262
logger.info("Running migrations...")
63+
64+
const migrator = new Migrator({ container })
65+
await migrator.ensureMigrationsTable()
66+
6367
await medusaAppLoader.runModulesMigrations({
6468
action: "run",
6569
})

packages/medusa/src/commands/db/rollback.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { MedusaAppLoader } from "@medusajs/framework"
1+
import { MedusaAppLoader, Migrator } from "@medusajs/framework"
22
import { LinkLoader } from "@medusajs/framework/links"
33
import {
44
ContainerRegistrationKeys,
@@ -40,6 +40,10 @@ const main = async function ({ directory, modules }) {
4040
* Reverting migrations
4141
*/
4242
logger.info("Reverting migrations...")
43+
44+
const migrator = new Migrator({ container })
45+
await migrator.ensureMigrationsTable()
46+
4347
await medusaAppLoader.runModulesMigrations({
4448
moduleNames: modules,
4549
action: "revert",

packages/modules/index/integration-tests/__tests__/config-sync.spec.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ import {
33
container,
44
logger,
55
MedusaAppLoader,
6+
Migrator,
67
} from "@medusajs/framework"
8+
import { asValue } from "@medusajs/framework/awilix"
79
import { MedusaAppOutput, MedusaModule } from "@medusajs/framework/modules-sdk"
810
import { ContainerRegistrationKeys, Modules } from "@medusajs/framework/utils"
911
import { initDb, TestDatabaseUtils } from "@medusajs/test-utils"
1012
import { IndexTypes, ModulesSdkTypes } from "@medusajs/types"
1113
import { Configuration } from "@utils"
12-
import { asValue } from "@medusajs/framework/awilix"
1314
import path from "path"
1415
import { setTimeout } from "timers/promises"
1516
import { EventBusServiceMock } from "../__fixtures__"
@@ -49,6 +50,10 @@ const beforeAll_ = async () => {
4950
medusaAppLoader = new MedusaAppLoader()
5051

5152
// Migrations
53+
54+
const migrator = new Migrator({ container })
55+
await migrator.ensureMigrationsTable()
56+
5257
await medusaAppLoader.runModulesMigrations()
5358
const linkPlanner = await medusaAppLoader.getLinksExecutionPlanner()
5459
const plan = await linkPlanner.createPlan()

0 commit comments

Comments
 (0)