Skip to content

Commit 6446325

Browse files
joe4devclaude
andcommitted
fix(init): stop retaining platform events in memory (NoOpEventsAPI embed)
LocalStackEventsAPI embedded StandaloneEventsAPI, which appends every platform event (START, runtimeDone, end, report, ...) to an in-memory event log whose only drain, FetchTailLogs, is never called in this deployment — i.e. unbounded memory growth in warm environments, measured at ~2.2 KB RSS per warm invoke (+41 MB over 20k invokes). Embed NoOpEventsAPI instead (the upstream sandbox default before this branch): same interop.EventsAPI surface, nothing retained. The overridden lifecycle hooks keep their LocalStack-specific behavior and simply no longer forward to the standalone event log. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent fde9640 commit 6446325

1 file changed

Lines changed: 11 additions & 9 deletions

File tree

cmd/localstack/events.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66

77
"github.com/aws/aws-lambda-runtime-interface-emulator/internal/lambda/fatalerror"
88
"github.com/aws/aws-lambda-runtime-interface-emulator/internal/lambda/interop"
9-
"github.com/aws/aws-lambda-runtime-interface-emulator/internal/lambda/rapidcore/standalone/telemetry"
109
lambdatelemetry "github.com/aws/aws-lambda-runtime-interface-emulator/internal/lambda/telemetry"
1110
)
1211

@@ -31,7 +30,11 @@ import (
3130
// doRuntimeDomainInit and so runs first on the deferred LIFO unwind. All three fire even when
3231
// the init is aborted by a reset or dies before the runtime starts.
3332
type LocalStackEventsAPI struct {
34-
*telemetry.StandaloneEventsAPI
33+
// NoOpEventsAPI satisfies the rest of interop.EventsAPI without retaining anything.
34+
// Do not embed StandaloneEventsAPI here: it appends every platform event to an
35+
// in-memory event log that is only drained via FetchTailLogs, which this deployment
36+
// never calls — i.e. unbounded memory growth in warm environments.
37+
lambdatelemetry.NoOpEventsAPI
3538
logCollector *LogCollector
3639
// onDemand mirrors CustomInteropServer.onDemand: only on-demand functions report the
3740
// cold-start init duration in their first invocation's REPORT line (AWS omits it for
@@ -65,17 +68,16 @@ type LocalStackEventsAPI struct {
6568

6669
func NewLocalStackEventsAPI(logCollector *LogCollector, onDemand bool) *LocalStackEventsAPI {
6770
return &LocalStackEventsAPI{
68-
StandaloneEventsAPI: new(telemetry.StandaloneEventsAPI),
69-
logCollector: logCollector,
70-
onDemand: onDemand,
71+
logCollector: logCollector,
72+
onDemand: onDemand,
7173
}
7274
}
7375

7476
func (e *LocalStackEventsAPI) SendInitStart(data interop.InitStartData) error {
7577
e.mu.Lock()
7678
e.lastInitStatus, e.lastInitErrorType = "", ""
7779
e.mu.Unlock()
78-
return e.StandaloneEventsAPI.SendInitStart(data)
80+
return nil
7981
}
8082

8183
func (e *LocalStackEventsAPI) SendInitRuntimeDone(data interop.InitRuntimeDoneData) error {
@@ -86,7 +88,7 @@ func (e *LocalStackEventsAPI) SendInitRuntimeDone(data interop.InitRuntimeDoneDa
8688
e.lastInitErrorType = *data.ErrorType
8789
}
8890
e.mu.Unlock()
89-
return e.StandaloneEventsAPI.SendInitRuntimeDone(data)
91+
return nil
9092
}
9193

9294
func (e *LocalStackEventsAPI) SendInitReport(data interop.InitReportData) error {
@@ -122,12 +124,12 @@ func (e *LocalStackEventsAPI) SendInitReport(data interop.InitReportData) error
122124
if line != "" {
123125
_, _ = e.logCollector.Write([]byte(line))
124126
}
125-
return e.StandaloneEventsAPI.SendInitReport(data)
127+
return nil
126128
}
127129

128130
func (e *LocalStackEventsAPI) SendInvokeStart(data interop.InvokeStartData) error {
129131
_, _ = fmt.Fprintf(e.logCollector, "START RequestId: %s Version: %s\n", data.RequestID, data.Version)
130-
return e.StandaloneEventsAPI.SendInvokeStart(data)
132+
return nil
131133
}
132134

133135
// SetInitPhaseTimedOut marks the in-flight init phase as timed out by the RIE, so the

0 commit comments

Comments
 (0)