@@ -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,37 @@ 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 ( )
2145app . engine ( 'pug' , pug . __express )
2246
47+ app . get ( '/metrics' , async ( req , res ) => {
48+ res . set ( 'Content-Type' , register . contentType )
49+ res . end ( await register . metrics ( ) )
50+ } )
51+
2352if ( app . settings . env == 'development' )
2453 app . use ( require ( 'morgan' ) ( 'dev' ) )
2554
@@ -37,25 +66,65 @@ app.use((req, res, next) => {
3766 if ( ! langs . includes ( lang ) ) lang = 'en'
3867 if ( req . query . lang && req . cookies . lang !== lang ) res . cookie ( 'lang' , lang )
3968
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 )
69+ if ( typeof process . send === 'function' ) {
70+ const requestId = ++ requestCounter
71+ process . send ( { type : 'startRender' , requestId } )
72+ let responded = false
73+ const handler = ( msg ) => {
74+ if ( msg . requestId === requestId && ! responded ) {
75+ responded = true
76+ clearTimeout ( timeout )
77+ process . removeListener ( 'message' , handler )
78+ if ( msg . type === 'renderAllowed' ) {
79+ doRender ( )
80+ } else if ( msg . type === 'renderDenied' ) {
81+ res . status ( 503 ) . send ( 'Server overloaded' )
82+ }
83+ }
4684 }
85+ process . on ( 'message' , handler )
86+ const timeout = setTimeout ( ( ) => {
87+ if ( ! responded ) {
88+ responded = true
89+ process . removeListener ( 'message' , handler )
90+ console . error ( 'IPC timeout for request' , requestId )
91+ res . status ( 500 ) . send ( 'Internal server error' )
92+ }
93+ } , 5000 ) // 5 second timeout
94+ } else {
95+ doRender ( )
96+ }
4797
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- } )
98+ function doRender ( ) {
99+ activeRenders . inc ( )
100+ const end = renderDuration . startTimer ( )
101+ let metricsUpdated = false
102+ render ( req . _parsedUrl . pathname , req . _parsedUrl . query || '' , req . body , { theme, lang, isHead : req . method === 'HEAD' } , ( err , resp ) => {
103+ if ( ! metricsUpdated ) {
104+ metricsUpdated = true
105+ if ( typeof process . send === 'function' ) process . send ( { type : 'endRender' } )
106+ activeRenders . dec ( )
107+ end ( )
108+ totalRenders . inc ( )
109+ }
110+ if ( err ) return next ( err )
111+ if ( resp . redirect ) return res . redirect ( 301 , baseHref + resp . redirect . substr ( 1 ) )
112+ if ( resp . errorCode ) {
113+ console . error ( `Failed with code ${ resp . errorCode } :` , resp )
114+ return res . sendStatus ( resp . errorCode )
115+ }
58116
117+ res . status ( resp . status || 200 )
118+ res . render ( indexView , {
119+ prerender_title : resp . title
120+ , prerender_html : resp . html
121+ , canon_url : canonBase ? canonBase + req . url : null
122+ , noscript : true
123+ , theme
124+ , t : l10n [ lang ]
125+ } )
126+ } )
127+ }
59128} )
60129
61130// Cleanup socket file from previous executions
0 commit comments