chore: medusa shutdown (#6865)
* chore: medusa shutdown * continue * use shutdown * on application shutdown * consume shutdown * more connection close * more cleanup * more cleanup * update lock * revert package * graceful shutdown * Create yellow-apples-attack.md * graceful shutdown * graceful shutdown --------- Co-authored-by: Sebastian Rindom <skrindom@gmail.com> Co-authored-by: Riqwan Thamir <rmthamir@gmail.com>
This commit is contained in:
co-authored by
Sebastian Rindom
Riqwan Thamir
parent
0c0b425de7
commit
8fd1488938
@@ -54,7 +54,10 @@ export default async function ({ port, cpus, directory }) {
|
||||
|
||||
const app = express()
|
||||
|
||||
const { dbConnection } = await loaders({ directory, expressApp: app })
|
||||
const { dbConnection, shutdown } = await loaders({
|
||||
directory,
|
||||
expressApp: app,
|
||||
})
|
||||
const serverActivity = Logger.activity(`Creating server`)
|
||||
const server = GracefulShutdownServer.create(
|
||||
app.listen(port, (err) => {
|
||||
@@ -70,7 +73,9 @@ export default async function ({ port, cpus, directory }) {
|
||||
server
|
||||
.shutdown()
|
||||
.then(() => {
|
||||
process.exit(0)
|
||||
shutdown().then(() => {
|
||||
process.exit(0)
|
||||
})
|
||||
})
|
||||
.catch((e) => {
|
||||
process.exit(1)
|
||||
|
||||
@@ -19,14 +19,13 @@ export default async function ({ port, directory }) {
|
||||
const app = express()
|
||||
|
||||
try {
|
||||
const { dbConnection, configModule, container } = await loaders({
|
||||
const { dbConnection, shutdown } = await loaders({
|
||||
directory,
|
||||
expressApp: app,
|
||||
})
|
||||
|
||||
let server
|
||||
const serverActivity = Logger.activity(`Creating server`)
|
||||
server = GracefulShutdownServer.create(
|
||||
const server = GracefulShutdownServer.create(
|
||||
app.listen(port, (err) => {
|
||||
if (err) {
|
||||
return
|
||||
@@ -42,7 +41,9 @@ export default async function ({ port, directory }) {
|
||||
.shutdown()
|
||||
.then(() => {
|
||||
Logger.info("Gracefully stopping the server.")
|
||||
process.exit(0)
|
||||
shutdown().then(() => {
|
||||
process.exit(0)
|
||||
})
|
||||
})
|
||||
.catch((e) => {
|
||||
Logger.error("Error received when shutting down the server.", e)
|
||||
|
||||
@@ -11,7 +11,10 @@ type Options = {
|
||||
configModule: ConfigModule
|
||||
}
|
||||
|
||||
export default async ({ app, configModule }: Options): Promise<Express> => {
|
||||
export default async ({ app, configModule }: Options): Promise<{
|
||||
app: Express,
|
||||
shutdown: () => Promise<void>
|
||||
}> => {
|
||||
let sameSite: string | boolean = false
|
||||
let secure = false
|
||||
if (
|
||||
@@ -38,9 +41,11 @@ export default async ({ app, configModule }: Options): Promise<Express> => {
|
||||
store: null,
|
||||
}
|
||||
|
||||
let redisClient
|
||||
|
||||
if (configModule?.projectConfig?.redis_url) {
|
||||
const RedisStore = createStore(session)
|
||||
const redisClient = new Redis(
|
||||
redisClient = new Redis(
|
||||
configModule.projectConfig.redis_url,
|
||||
configModule.projectConfig.redis_options ?? {}
|
||||
)
|
||||
@@ -63,5 +68,9 @@ export default async ({ app, configModule }: Options): Promise<Express> => {
|
||||
res.status(200).send("OK")
|
||||
})
|
||||
|
||||
return app
|
||||
const shutdown = async () => {
|
||||
redisClient?.disconnect()
|
||||
}
|
||||
|
||||
return { app, shutdown }
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
import { ConfigModule, MODULE_RESOURCE_TYPE } from "@medusajs/types"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
MedusaV2Flag,
|
||||
isString,
|
||||
MedusaV2Flag, promiseAll,
|
||||
} from "@medusajs/utils"
|
||||
import { asValue } from "awilix"
|
||||
import { Express, NextFunction, Request, Response } from "express"
|
||||
@@ -97,18 +97,21 @@ async function loadMedusaV2({
|
||||
|
||||
container.register({
|
||||
[ContainerRegistrationKeys.LOGGER]: asValue(Logger),
|
||||
featureFlagRouter: asValue(featureFlagRouter),
|
||||
[ContainerRegistrationKeys.FEATURE_FLAG_ROUTER]: asValue(featureFlagRouter),
|
||||
[ContainerRegistrationKeys.CONFIG_MODULE]: asValue(configModule),
|
||||
["remoteQuery"]: asValue(null),
|
||||
[ContainerRegistrationKeys.REMOTE_QUERY]: asValue(null),
|
||||
})
|
||||
|
||||
await loadMedusaApp({
|
||||
const { onApplicationShutdown: medusaAppOnApplicationShutdown } = await loadMedusaApp({
|
||||
configModule,
|
||||
container,
|
||||
})
|
||||
|
||||
let expressShutdown = async () => {}
|
||||
|
||||
if (shouldStartAPI) {
|
||||
await expressLoader({ app: expressApp, configModule })
|
||||
const { shutdown } = await expressLoader({ app: expressApp, configModule })
|
||||
expressShutdown = shutdown
|
||||
|
||||
expressApp.use((req: Request, res: Response, next: NextFunction) => {
|
||||
req.scope = container.createScope() as MedusaContainer
|
||||
@@ -145,11 +148,21 @@ async function loadMedusaV2({
|
||||
|
||||
await createDefaultsWorkflow(container).run()
|
||||
|
||||
const shutdown = async () => {
|
||||
await promiseAll([
|
||||
container.dispose(),
|
||||
pgConnection?.context?.destroy(),
|
||||
expressShutdown(),
|
||||
medusaAppOnApplicationShutdown()
|
||||
])
|
||||
}
|
||||
|
||||
return {
|
||||
configModule,
|
||||
container,
|
||||
app: expressApp,
|
||||
pgConnection,
|
||||
shutdown,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,6 +176,7 @@ export default async ({
|
||||
dbConnection?: Connection
|
||||
app: Express
|
||||
pgConnection: unknown
|
||||
shutdown: () => Promise<void>
|
||||
}> => {
|
||||
const configModule = loadConfig(rootDirectory)
|
||||
const featureFlagRouter = featureFlagsLoader(configModule, Logger)
|
||||
@@ -197,7 +211,7 @@ export default async ({
|
||||
featureFlagRouter: asValue(featureFlagRouter),
|
||||
})
|
||||
|
||||
await redisLoader({ container, configModule, logger: Logger })
|
||||
const { shutdown: redisShutdown } = await redisLoader({ container, configModule, logger: Logger })
|
||||
|
||||
const modelsActivity = Logger.activity(`Initializing models${EOL}`)
|
||||
track("MODELS_INIT_STARTED")
|
||||
@@ -257,7 +271,7 @@ export default async ({
|
||||
track("MODULES_INIT_STARTED")
|
||||
|
||||
// Move before services init once all modules are migrated and do not rely on core resources anymore
|
||||
await loadMedusaApp({
|
||||
const { onApplicationShutdown: medusaAppOnApplicationShutdown } = await loadMedusaApp({
|
||||
configModule,
|
||||
container,
|
||||
})
|
||||
@@ -267,7 +281,7 @@ export default async ({
|
||||
|
||||
const expActivity = Logger.activity(`Initializing express${EOL}`)
|
||||
track("EXPRESS_INIT_STARTED")
|
||||
await expressLoader({ app: expressApp, configModule })
|
||||
const { shutdown: expressShutdown } = await expressLoader({ app: expressApp, configModule })
|
||||
await passportLoader({ app: expressApp, configModule })
|
||||
const exAct = Logger.success(expActivity, "Express intialized") || {}
|
||||
track("EXPRESS_INIT_COMPLETED", { duration: exAct.duration })
|
||||
@@ -324,11 +338,23 @@ export default async ({
|
||||
Logger.success(searchActivity, "Indexing event emitted") || {}
|
||||
track("SEARCH_ENGINE_INDEXING_COMPLETED", { duration: searchAct.duration })
|
||||
|
||||
async function shutdown() {
|
||||
await promiseAll([
|
||||
container.dispose(),
|
||||
dbConnection?.destroy(),
|
||||
pgConnection?.context?.destroy(),
|
||||
redisShutdown(),
|
||||
expressShutdown(),
|
||||
medusaAppOnApplicationShutdown(),
|
||||
])
|
||||
}
|
||||
|
||||
return {
|
||||
configModule,
|
||||
container,
|
||||
dbConnection,
|
||||
app: expressApp,
|
||||
pgConnection,
|
||||
shutdown,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +249,6 @@ export async function runModulesLoader({
|
||||
}
|
||||
container: MedusaContainer
|
||||
}): Promise<void> {
|
||||
const featureFlagRouter = container.resolve<FlagRouter>("featureFlagRouter")
|
||||
const injectedDependencies = {
|
||||
[ContainerRegistrationKeys.PG_CONNECTION]: container.resolve(
|
||||
ContainerRegistrationKeys.PG_CONNECTION
|
||||
|
||||
@@ -15,23 +15,25 @@ async function redisLoader({
|
||||
container,
|
||||
configModule,
|
||||
logger,
|
||||
}: Options): Promise<void> {
|
||||
}: Options): Promise<{ shutdown: () => Promise<void> }> {
|
||||
let client!: Redis | FakeRedis
|
||||
|
||||
if (configModule.projectConfig.redis_url) {
|
||||
const redisClient = new Redis(configModule.projectConfig.redis_url, {
|
||||
client = new Redis(configModule.projectConfig.redis_url, {
|
||||
// Lazy connect to properly handle connection errors
|
||||
lazyConnect: true,
|
||||
...(configModule.projectConfig.redis_options ?? {}),
|
||||
})
|
||||
|
||||
try {
|
||||
await redisClient.connect()
|
||||
await client.connect()
|
||||
logger?.info(`Connection to Redis established`)
|
||||
} catch (err) {
|
||||
logger?.error(`An error occurred while connecting to Redis:${EOL} ${err}`)
|
||||
}
|
||||
|
||||
container.register({
|
||||
redisClient: asValue(redisClient),
|
||||
redisClient: asValue(client),
|
||||
})
|
||||
} else {
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
@@ -43,12 +45,18 @@ async function redisLoader({
|
||||
logger.info("Using fake Redis")
|
||||
|
||||
// Economical way of dealing with redis clients
|
||||
const client = new FakeRedis()
|
||||
client = new FakeRedis()
|
||||
|
||||
container.register({
|
||||
redisClient: asValue(client),
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
shutdown: async () => {
|
||||
client.disconnect()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default redisLoader
|
||||
|
||||
Reference in New Issue
Block a user