mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Drain httpServer when apollo server stops
This commit is contained in:
parent
273ff9d4be
commit
ac775ae8f2
2 changed files with 31 additions and 26 deletions
|
|
@ -4,16 +4,24 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
import { createPrometheusExporterPlugin } from '@bmatei/apollo-prometheus-exporter'
|
||||
import { makeExecutableSchema } from '@graphql-tools/schema'
|
||||
import * as Sentry from '@sentry/node'
|
||||
import { ContextFunction, PluginDefinition } from 'apollo-server-core'
|
||||
import { Express } from 'express'
|
||||
import {
|
||||
ApolloServerPluginDrainHttpServer,
|
||||
ContextFunction,
|
||||
PluginDefinition,
|
||||
} from 'apollo-server-core'
|
||||
import { ApolloServer } from 'apollo-server-express'
|
||||
import { ExpressContext } from 'apollo-server-express/dist/ApolloServer'
|
||||
import { ApolloServerPlugin } from 'apollo-server-plugin-base'
|
||||
import { Express } from 'express'
|
||||
import * as httpContext from 'express-http-context2'
|
||||
import type http from 'http'
|
||||
import * as jwt from 'jsonwebtoken'
|
||||
import { EntityManager } from 'typeorm'
|
||||
import { promisify } from 'util'
|
||||
import { ReadingProgressDataSource } from './datasources/reading_progress_data_source'
|
||||
import { appDataSource } from './data_source'
|
||||
import { sanitizeDirectiveTransformer } from './directives'
|
||||
import { env } from './env'
|
||||
|
|
@ -22,17 +30,14 @@ import { functionResolvers } from './resolvers/function_resolvers'
|
|||
import { ClaimsToSet, RequestContext, ResolverContext } from './resolvers/types'
|
||||
import ScalarResolvers from './scalars'
|
||||
import typeDefs from './schema'
|
||||
import { tracer } from './tracing'
|
||||
import { getClaimsByToken, setAuthInCookie } from './utils/auth'
|
||||
import { SetClaimsRole } from './utils/dictionary'
|
||||
import { logger } from './utils/logger'
|
||||
import { ReadingProgressDataSource } from './datasources/reading_progress_data_source'
|
||||
import { createPrometheusExporterPlugin } from '@bmatei/apollo-prometheus-exporter'
|
||||
import { ApolloServerPlugin } from 'apollo-server-plugin-base'
|
||||
import {
|
||||
countDailyServiceUsage,
|
||||
createServiceUsage,
|
||||
} from './services/service_usage'
|
||||
import { tracer } from './tracing'
|
||||
import { getClaimsByToken, setAuthInCookie } from './utils/auth'
|
||||
import { SetClaimsRole } from './utils/dictionary'
|
||||
import { logger } from './utils/logger'
|
||||
|
||||
const signToken = promisify(jwt.sign)
|
||||
const pubsub = createPubSubClient()
|
||||
|
|
@ -100,7 +105,10 @@ const contextFunc: ContextFunction<ExpressContext, ResolverContext> = async ({
|
|||
return ctx
|
||||
}
|
||||
|
||||
export function makeApolloServer(app: Express): ApolloServer {
|
||||
export function makeApolloServer(
|
||||
app: Express,
|
||||
httpServer: http.Server
|
||||
): ApolloServer {
|
||||
let schema = makeExecutableSchema({
|
||||
resolvers,
|
||||
typeDefs,
|
||||
|
|
@ -169,7 +177,14 @@ export function makeApolloServer(app: Express): ApolloServer {
|
|||
const apollo = new ApolloServer({
|
||||
schema: schema,
|
||||
context: contextFunc,
|
||||
plugins: [promExporter, usageLimitPlugin],
|
||||
plugins: [
|
||||
// Our httpServer handles incoming requests to our Express app.
|
||||
// Below, we tell Apollo Server to "drain" this httpServer,
|
||||
// enabling our servers to shut down gracefully.
|
||||
ApolloServerPluginDrainHttpServer({ httpServer }),
|
||||
promExporter,
|
||||
usageLimitPlugin,
|
||||
],
|
||||
formatError: (err) => {
|
||||
logger.info('server error', err)
|
||||
Sentry.captureException(err)
|
||||
|
|
|
|||
|
|
@ -4,13 +4,12 @@
|
|||
/* eslint-disable @typescript-eslint/no-misused-promises */
|
||||
import * as lw from '@google-cloud/logging-winston'
|
||||
import * as Sentry from '@sentry/node'
|
||||
import { ApolloServer } from 'apollo-server-express'
|
||||
import { json, urlencoded } from 'body-parser'
|
||||
import cookieParser from 'cookie-parser'
|
||||
import express, { Express } from 'express'
|
||||
import * as httpContext from 'express-http-context2'
|
||||
import promBundle from 'express-prom-bundle'
|
||||
import { createServer, Server } from 'http'
|
||||
import { createServer } from 'http'
|
||||
import * as prom from 'prom-client'
|
||||
import { config, loggers } from 'winston'
|
||||
import { makeApolloServer } from './apollo'
|
||||
|
|
@ -150,7 +149,8 @@ const main = async (): Promise<void> => {
|
|||
}
|
||||
|
||||
const app = createApp()
|
||||
const apollo = makeApolloServer(app)
|
||||
const httpServer = createServer(app)
|
||||
const apollo = makeApolloServer(app, httpServer)
|
||||
await apollo.start()
|
||||
apollo.applyMiddleware({ app, path: '/api/graphql', cors: corsConfig })
|
||||
|
||||
|
|
@ -159,7 +159,7 @@ const main = async (): Promise<void> => {
|
|||
const mw = await lw.express.makeMiddleware(mwLogger, transport)
|
||||
app.use(mw)
|
||||
|
||||
const listener = app.listen({ port: PORT }, async () => {
|
||||
const listener = httpServer.listen({ port: PORT }, async () => {
|
||||
const logger = buildLogger('app.dispatch')
|
||||
logger.notice(`🚀 Server ready at ${apollo.graphqlPath}`)
|
||||
})
|
||||
|
|
@ -176,22 +176,12 @@ const main = async (): Promise<void> => {
|
|||
const gracefulShutdown = async (signal: string) => {
|
||||
console.log(`[api]: Received ${signal}, closing server...`)
|
||||
await apollo.stop()
|
||||
console.log('[api]: Apollo server stopped')
|
||||
console.log('[api]: Express server stopped')
|
||||
|
||||
console.log('[posthog]: flushing events')
|
||||
await analytics.shutdownAsync()
|
||||
console.log('[posthog]: events flushed')
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
listener.close((err) => {
|
||||
console.log('[api]: Express listener closed')
|
||||
if (err) {
|
||||
console.log('[api]: error stopping listener', { err })
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
|
||||
// Shutdown redis before DB because the quit sequence can
|
||||
// cause appDataSource to get reloaded in the callback
|
||||
await redisDataSource.shutdown()
|
||||
|
|
|
|||
Loading…
Reference in a new issue