Skip to content

Commit 2c52c1e

Browse files
committed
fix: separate init and invoke durations
1 parent 1668238 commit 2c52c1e

2 files changed

Lines changed: 63 additions & 15 deletions

File tree

internal/lambda/rie/handlers.go

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,21 @@ func printEndReports(invokeId string, initDuration string, memorySize string, in
7979
invokeId, invokeDuration, math.Ceil(invokeDuration), memorySize, memorySize)
8080
}
8181

82-
func formatInitDuration(sandbox Sandbox, initStart time.Time, timeoutDuration time.Duration) string {
83-
if initStart.IsZero() {
84-
return ""
82+
func startInitOnce(sandbox Sandbox, functionVersion string, timeout int64, bs interop.Bootstrap) time.Time {
83+
initMutex.Lock()
84+
defer initMutex.Unlock()
85+
86+
if initDone {
87+
return time.Time{}
8588
}
8689

87-
initEnd := sandbox.AwaitInitCompletion()
88-
if initEnd.IsZero() {
90+
initStart := InitHandler(sandbox, functionVersion, timeout, bs)
91+
initDone = true
92+
return initStart
93+
}
94+
95+
func formatInitDuration(initStart time.Time, initEnd time.Time, timeoutDuration time.Duration) string {
96+
if initStart.IsZero() || initEnd.IsZero() {
8997
return ""
9098
}
9199

@@ -121,14 +129,7 @@ func InvokeHandler(w http.ResponseWriter, r *http.Request, sandbox Sandbox, bs i
121129
functionVersion := GetenvWithDefault("AWS_LAMBDA_FUNCTION_VERSION", "$LATEST")
122130
memorySize := GetenvWithDefault("AWS_LAMBDA_FUNCTION_MEMORY_SIZE", "3008")
123131

124-
var initStart time.Time
125-
initMutex.Lock()
126-
if !initDone {
127-
initStart = InitHandler(sandbox, functionVersion, timeout, bs)
128-
// Set initDone so next invokes do not try to Init the function again
129-
initDone = true
130-
}
131-
initMutex.Unlock()
132+
initStart := startInitOnce(sandbox, functionVersion, timeout, bs)
132133

133134
invokeStart := time.Now()
134135
invokeID := r.Header.Get("X-Amzn-RequestId")
@@ -211,7 +212,8 @@ func InvokeHandler(w http.ResponseWriter, r *http.Request, sandbox Sandbox, bs i
211212
w.WriteHeader(http.StatusGatewayTimeout)
212213
return
213214
case rapidcore.ErrInvokeTimeout:
214-
printEndReports(invokePayload.ID, formatInitDuration(sandbox, initStart, timeoutDuration), memorySize, invokeStart, timeoutDuration)
215+
initEnd := sandbox.AwaitInitCompletion()
216+
printEndReports(invokePayload.ID, formatInitDuration(initStart, initEnd, timeoutDuration), memorySize, invokeStart, timeoutDuration)
215217

216218
w.Write([]byte(fmt.Sprintf("Task timed out after %d.00 seconds", timeout)))
217219
time.Sleep(100 * time.Millisecond)
@@ -220,7 +222,12 @@ func InvokeHandler(w http.ResponseWriter, r *http.Request, sandbox Sandbox, bs i
220222
}
221223
}
222224

223-
printEndReports(invokePayload.ID, formatInitDuration(sandbox, initStart, timeoutDuration), memorySize, invokeStart, timeoutDuration)
225+
initEnd := sandbox.AwaitInitCompletion()
226+
initDuration := formatInitDuration(initStart, initEnd, timeoutDuration)
227+
if !initStart.IsZero() && initEnd.After(invokeStart) {
228+
invokeStart = initEnd
229+
}
230+
printEndReports(invokePayload.ID, initDuration, memorySize, invokeStart, timeoutDuration)
224231

225232
if invokeResp.StatusCode != 0 {
226233
w.WriteHeader(invokeResp.StatusCode)

internal/lambda/rie/handlers_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,41 @@ func (s *delayedInitSandbox) Invoke(http.ResponseWriter, *interop.Invoke) error
3939
return nil
4040
}
4141

42+
type panicInitSandbox struct {
43+
delayedInitSandbox
44+
}
45+
46+
func (s *panicInitSandbox) Init(*interop.Init, int64) {
47+
panic("init failed")
48+
}
49+
50+
func TestStartInitOnceReleasesLockAfterPanic(t *testing.T) {
51+
initMutex.Lock()
52+
initDone = false
53+
initMutex.Unlock()
54+
t.Cleanup(func() {
55+
initMutex.Lock()
56+
initDone = false
57+
initMutex.Unlock()
58+
})
59+
60+
func() {
61+
defer func() { require.Equal(t, "init failed", recover()) }()
62+
startInitOnce(&panicInitSandbox{}, "$LATEST", 1, nil)
63+
}()
64+
65+
initStarted := make(chan struct{})
66+
go func() {
67+
startInitOnce(&delayedInitSandbox{}, "$LATEST", 1, nil)
68+
close(initStarted)
69+
}()
70+
select {
71+
case <-initStarted:
72+
case <-time.After(time.Second):
73+
require.Fail(t, "init mutex remained locked after panic")
74+
}
75+
}
76+
4277
func TestInvokeHandlerReportsRuntimeInitDuration(t *testing.T) {
4378
initMutex.Lock()
4479
initDone = false
@@ -70,4 +105,10 @@ func TestInvokeHandlerReportsRuntimeInitDuration(t *testing.T) {
70105
durationMilliseconds, err := strconv.ParseFloat(matches[1], 64)
71106
require.NoError(t, err)
72107
require.GreaterOrEqual(t, durationMilliseconds, float64(40))
108+
109+
matches = regexp.MustCompile(`\tDuration: ([0-9.]+) ms`).FindStringSubmatch(string(output))
110+
require.Len(t, matches, 2)
111+
durationMilliseconds, err = strconv.ParseFloat(matches[1], 64)
112+
require.NoError(t, err)
113+
require.Less(t, durationMilliseconds, float64(40))
73114
}

0 commit comments

Comments
 (0)