@@ -3,6 +3,7 @@ import pug from 'pug'
33import path from 'path'
44import express from 'express'
55import request from 'superagent'
6+ import promClient from 'prom-client'
67
78import l10n from '../client/l10n'
89import render from '../client/run-server'
@@ -17,9 +18,38 @@ const rpath = p => path.join(__dirname, p)
1718
1819const indexView = rpath ( '../../client/index.pug' )
1920
21+ const register = new promClient . Registry ( )
22+
23+ const activeRenders = new promClient . Gauge ( {
24+ name : 'prerender_active_renders' ,
25+ help : 'Number of active renders'
26+ } )
27+
28+ const totalRenders = new promClient . Counter ( {
29+ name : 'prerender_total_renders' ,
30+ help : 'Total number of renders completed'
31+ } )
32+
33+ const renderDuration = new promClient . Histogram ( {
34+ name : 'prerender_render_duration_seconds' ,
35+ help : 'Duration of renders in seconds'
36+ } )
37+
38+ register . registerMetric ( activeRenders )
39+ register . registerMetric ( totalRenders )
40+ register . registerMetric ( renderDuration )
41+
42+ let requestCounter = 0
43+
2044const app = express ( )
45+ app . set ( 'trust proxy' , true )
2146app . engine ( 'pug' , pug . __express )
2247
48+ app . get ( '/metrics' , async ( req , res ) => {
49+ res . set ( 'Content-Type' , register . contentType )
50+ res . end ( await register . metrics ( ) )
51+ } )
52+
2353if ( app . settings . env == 'development' )
2454 app . use ( require ( 'morgan' ) ( 'dev' ) )
2555
@@ -37,25 +67,65 @@ app.use((req, res, next) => {
3767 if ( ! langs . includes ( lang ) ) lang = 'en'
3868 if ( req . query . lang && req . cookies . lang !== lang ) res . cookie ( 'lang' , lang )
3969
40- render ( req . _parsedUrl . pathname , req . _parsedUrl . query || '' , req . body , { theme, lang, isHead : req . method === 'HEAD' } , ( err , resp ) => {
41- if ( err ) return next ( err )
42- if ( resp . redirect ) return res . redirect ( 301 , baseHref + resp . redirect . substr ( 1 ) )
43- if ( resp . errorCode ) {
44- console . error ( `Failed with code ${ resp . errorCode } :` , resp )
45- return res . sendStatus ( resp . errorCode )
70+ if ( typeof process . send === 'function' ) {
71+ const requestId = ++ requestCounter
72+ process . send ( { type : 'startRender' , requestId } )
73+ let responded = false
74+ const handler = ( msg ) => {
75+ if ( msg . requestId === requestId && ! responded ) {
76+ responded = true
77+ clearTimeout ( timeout )
78+ process . removeListener ( 'message' , handler )
79+ if ( msg . type === 'renderAllowed' ) {
80+ doRender ( )
81+ } else if ( msg . type === 'renderDenied' ) {
82+ res . status ( 503 ) . send ( 'Server overloaded' )
83+ }
84+ }
4685 }
86+ process . on ( 'message' , handler )
87+ const timeout = setTimeout ( ( ) => {
88+ if ( ! responded ) {
89+ responded = true
90+ process . removeListener ( 'message' , handler )
91+ console . error ( 'IPC timeout for request' , requestId )
92+ res . status ( 500 ) . send ( 'Internal server error' )
93+ }
94+ } , 5000 ) // 5 second timeout
95+ } else {
96+ doRender ( )
97+ }
4798
48- res . status ( resp . status || 200 )
49- res . render ( indexView , {
50- prerender_title : resp . title
51- , prerender_html : resp . html
52- , canon_url : canonBase ? canonBase + req . url : null
53- , noscript : true
54- , theme
55- , t : l10n [ lang ]
56- } )
57- } )
99+ function doRender ( ) {
100+ activeRenders . inc ( )
101+ const end = renderDuration . startTimer ( )
102+ let metricsUpdated = false
103+ render ( req . _parsedUrl . pathname , req . _parsedUrl . query || '' , req . body , { theme, lang, isHead : req . method === 'HEAD' } , ( err , resp ) => {
104+ if ( ! metricsUpdated ) {
105+ metricsUpdated = true
106+ if ( typeof process . send === 'function' ) process . send ( { type : 'endRender' } )
107+ activeRenders . dec ( )
108+ end ( )
109+ totalRenders . inc ( )
110+ }
111+ if ( err ) return next ( err )
112+ if ( resp . redirect ) return res . redirect ( 301 , baseHref + resp . redirect )
113+ if ( resp . errorCode ) {
114+ console . error ( `Failed with code ${ resp . errorCode } :` , resp )
115+ return res . sendStatus ( resp . errorCode )
116+ }
58117
118+ res . status ( resp . status || 200 )
119+ res . render ( indexView , {
120+ prerender_title : resp . title
121+ , prerender_html : resp . html
122+ , canon_url : canonBase ? canonBase + req . url : null
123+ , noscript : true
124+ , theme
125+ , t : l10n [ lang ]
126+ } )
127+ } )
128+ }
59129} )
60130
61131// Cleanup socket file from previous executions
0 commit comments