1
1
'use strict' ;
2
2
3
3
const debugLog = require ( './debugLog' ) ;
4
+ const fork = require ( 'child_process' ) . fork ;
5
+ const _ = require ( 'lodash' ) ;
6
+ const path = require ( 'path' ) ;
7
+ const uuid = require ( 'uuid/v4' ) ;
8
+
9
+ const handlerCache = { } ;
10
+ const messageCallbacks = { } ;
4
11
5
12
module . exports = {
6
13
getFunctionOptions ( fun , funName , servicePath ) {
@@ -18,9 +25,69 @@ module.exports = {
18
25
} ;
19
26
} ,
20
27
28
+ createExternalHandler ( funOptions , options ) {
29
+ let handlerContext = handlerCache [ funOptions . handlerPath ] ;
30
+
31
+ function handleFatal ( error ) {
32
+ debugLog ( `External handler receieved fatal error ${ JSON . stringify ( error ) } ` ) ;
33
+ handlerContext . inflight . forEach ( id => messageCallbacks [ id ] ( error ) ) ;
34
+ handlerContext . inflight . clear ( ) ;
35
+ delete handlerCache [ funOptions . handlerPath ] ;
36
+ }
37
+
38
+ if ( ! handlerContext ) {
39
+ debugLog ( `Loading external handler... (${ funOptions . handlerPath } )` ) ;
40
+
41
+ const helperPath = path . resolve ( __dirname , 'ipcHelper.js' ) ;
42
+ const ipcProcess = fork ( helperPath , [ funOptions . handlerPath ] , {
43
+ env : _ . omitBy ( process . env , _ . isUndefined ) ,
44
+ stdio : [ 0 , 1 , 2 , 'ipc' ] ,
45
+ } ) ;
46
+ handlerContext = { process : ipcProcess , inflight : new Set ( ) } ;
47
+ if ( options . skipCacheInvalidation ) {
48
+ handlerCache [ funOptions . handlerPath ] = handlerContext ;
49
+ }
50
+
51
+ ipcProcess . on ( 'message' , message => {
52
+ debugLog ( `External handler received message ${ JSON . stringify ( message ) } ` ) ;
53
+ if ( message . id ) {
54
+ messageCallbacks [ message . id ] ( message . error , message . ret ) ;
55
+ handlerContext . inflight . delete ( message . id ) ;
56
+ delete messageCallbacks [ message . id ] ;
57
+ }
58
+ else if ( message . error ) {
59
+ // Handler died!
60
+ handleFatal ( message . error ) ;
61
+ }
62
+
63
+ if ( ! options . skipCacheInvalidation ) {
64
+ handlerContext . process . kill ( ) ;
65
+ delete handlerCache [ funOptions . handlerPath ] ;
66
+ }
67
+ } ) ;
68
+
69
+ ipcProcess . on ( 'error' , error => handleFatal ( error ) ) ;
70
+ ipcProcess . on ( 'exit' , code => handleFatal ( `Handler process exited with code ${ code } ` ) ) ;
71
+ }
72
+ else {
73
+ debugLog ( `Using existing external handler for ${ funOptions . handlerPath } ` ) ;
74
+ }
75
+
76
+ return ( event , context , done ) => {
77
+ const id = uuid ( ) ;
78
+ messageCallbacks [ id ] = done ;
79
+ handlerContext . inflight . add ( id ) ;
80
+ handlerContext . process . send ( { id, name : funOptions . handlerName , event, context } ) ;
81
+ } ;
82
+ } ,
83
+
21
84
// Create a function handler
22
85
// The function handler is used to simulate Lambda functions
23
86
createHandler ( funOptions , options ) {
87
+ if ( options . useSeparateProcesses ) {
88
+ return this . createExternalHandler ( funOptions , options ) ;
89
+ }
90
+
24
91
if ( ! options . skipCacheInvalidation ) {
25
92
debugLog ( 'Invalidating cache...' ) ;
26
93
0 commit comments