@@ -29,8 +29,9 @@ func NewEngine(
2929 }
3030}
3131
32- func (m * Engine ) Run (ctx context.Context ) {
32+ func (e * Engine ) Run (ctx context.Context ) {
3333 updateTicker := time .NewTicker (1 * time .Second )
34+ removeTicker := time .NewTicker (60 * time .Second )
3435 deployTicker := time .NewTicker (60 * time .Second )
3536
3637 go func () {
@@ -40,101 +41,136 @@ func (m *Engine) Run(ctx context.Context) {
4041 updateTicker .Stop ()
4142 return
4243 case <- updateTicker .C :
43- lastUpdate := m .lastUpdate
44- m .lastUpdate = time .Now ().UTC ()
44+ lastUpdate := e .lastUpdate
45+ e .lastUpdate = time .Now ().UTC ()
4546
46- if err := m .populateCommands (ctx , lastUpdate ); err != nil {
47+ if err := e .populateCommands (ctx , lastUpdate ); err != nil {
4748 slog .Error (
4849 "Failed to populate commands in engine" ,
4950 slog .String ("error" , err .Error ()),
5051 )
5152 }
52- if err := m .populateEventListeners (ctx , lastUpdate ); err != nil {
53+ if err := e .populateEventListeners (ctx , lastUpdate ); err != nil {
5354 slog .Error (
5455 "Failed to populate event listeners in engine" ,
5556 slog .String ("error" , err .Error ()),
5657 )
5758 }
59+ case <- removeTicker .C :
60+ if err := e .removeDanglingCommands (ctx ); err != nil {
61+ slog .Error (
62+ "Failed to remove dangling commands in engine" ,
63+ slog .String ("error" , err .Error ()),
64+ )
65+ }
66+ if err := e .removeDanglingEventListeners (ctx ); err != nil {
67+ slog .Error (
68+ "Failed to remove dangling event listeners in engine" ,
69+ slog .String ("error" , err .Error ()),
70+ )
71+ }
5872 case <- deployTicker .C :
59- m .deployCommands (ctx )
73+ e .deployCommands (ctx )
6074 }
6175 }
6276 }()
6377}
6478
65- func (m * Engine ) populateCommands (ctx context.Context , lastUpdate time.Time ) error {
66- commandIDs , err := m .stores .CommandStore .EnabledCommandIDs (ctx )
67- if err != nil {
68- return fmt .Errorf ("failed to get enabled command IDs: %w" , err )
69- }
70-
71- commands , err := m .stores .CommandStore .EnabledCommandsUpdatedSince (ctx , lastUpdate )
79+ func (e * Engine ) populateCommands (ctx context.Context , lastUpdate time.Time ) error {
80+ commands , err := e .stores .CommandStore .EnabledCommandsUpdatedSince (ctx , lastUpdate )
7281 if err != nil {
7382 return fmt .Errorf ("failed to get commands: %w" , err )
7483 }
7584
76- m .Lock ()
77- defer m .Unlock ()
85+ lockStart := time .Now ()
86+ e .Lock ()
87+ defer e .Unlock ()
88+ lockDiff := time .Since (lockStart )
89+ if lockDiff > 250 * time .Millisecond {
90+ slog .Warn (
91+ "Locking engine for commands took too long" ,
92+ slog .String ("lock_duration" , lockDiff .String ()),
93+ )
94+ }
7895
7996 for _ , command := range commands {
80- app , ok := m .apps [command .AppID ]
97+ app , ok := e .apps [command .AppID ]
8198 if ! ok {
8299 app = NewApp (
83100 command .AppID ,
84- m .stores ,
101+ e .stores ,
85102 )
86- m .apps [command .AppID ] = app
103+ e .apps [command .AppID ] = app
87104 }
88105
89106 app .AddCommand (command )
90107 }
91108
92- for _ , app := range m .apps {
93- app .RemoveDanglingCommands (commandIDs )
94- }
95-
96109 return nil
97110}
98111
99- func (m * Engine ) populateEventListeners (ctx context.Context , lastUpdate time. Time ) error {
100- listenerIDs , err := m .stores .EventListenerStore . EnabledEventListenerIDs (ctx )
112+ func (e * Engine ) removeDanglingCommands (ctx context.Context ) error {
113+ commandIDs , err := e .stores .CommandStore . EnabledCommandIDs (ctx )
101114 if err != nil {
102- return fmt .Errorf ("failed to get enabled event listener IDs: %w" , err )
115+ return fmt .Errorf ("failed to get enabled command IDs: %w" , err )
103116 }
104117
105- listeners , err := m .stores .EventListenerStore .EnabledEventListenersUpdatedSince (ctx , lastUpdate )
118+ e .RLock ()
119+ defer e .RUnlock ()
120+
121+ for _ , app := range e .apps {
122+ app .RemoveDanglingCommands (commandIDs )
123+ }
124+
125+ return nil
126+ }
127+
128+ func (e * Engine ) populateEventListeners (ctx context.Context , lastUpdate time.Time ) error {
129+ listeners , err := e .stores .EventListenerStore .EnabledEventListenersUpdatedSince (ctx , lastUpdate )
106130 if err != nil {
107131 return fmt .Errorf ("failed to get event listeners: %w" , err )
108132 }
109133
110- m .Lock ()
111- defer m .Unlock ()
134+ e .Lock ()
135+ defer e .Unlock ()
112136
113137 for _ , listener := range listeners {
114- app , ok := m .apps [listener .AppID ]
138+ app , ok := e .apps [listener .AppID ]
115139 if ! ok {
116140 app = NewApp (
117141 listener .AppID ,
118- m .stores ,
142+ e .stores ,
119143 )
120- m .apps [listener .AppID ] = app
144+ e .apps [listener .AppID ] = app
121145 }
122146
123147 app .AddEventListener (listener )
124148 }
125149
126- for _ , app := range m .apps {
150+ return nil
151+ }
152+
153+ func (e * Engine ) removeDanglingEventListeners (ctx context.Context ) error {
154+ listenerIDs , err := e .stores .EventListenerStore .EnabledEventListenerIDs (ctx )
155+ if err != nil {
156+ return fmt .Errorf ("failed to get enabled event listener IDs: %w" , err )
157+ }
158+
159+ e .RLock ()
160+ defer e .RUnlock ()
161+
162+ for _ , app := range e .apps {
127163 app .RemoveDanglingEventListeners (listenerIDs )
128164 }
129165
130166 return nil
131167}
132168
133- func (m * Engine ) deployCommands (ctx context.Context ) {
134- m .Lock ()
135- defer m .Unlock ()
169+ func (e * Engine ) deployCommands (ctx context.Context ) {
170+ e .Lock ()
171+ defer e .Unlock ()
136172
137- for _ , app := range m .apps {
173+ for _ , app := range e .apps {
138174 if app .hasUndeployedChanges {
139175 go func () {
140176 ctx , cancel := context .WithTimeout (ctx , 10 * time .Second )
@@ -156,10 +192,20 @@ func (m *Engine) deployCommands(ctx context.Context) {
156192 }
157193}
158194
195+ // HandleEvent blocks until the event is handled by the corresponding app.
159196func (e * Engine ) HandleEvent (appID string , session * state.State , event gateway.Event ) {
197+ lockStart := time .Now ()
160198 e .RLock ()
161199 app := e .apps [appID ]
162200 e .RUnlock ()
201+ lockDiff := time .Since (lockStart )
202+ if lockDiff > 100 * time .Millisecond {
203+ slog .Warn (
204+ "Locking engine for event took too long" ,
205+ slog .String ("app_id" , appID ),
206+ slog .String ("lock_duration" , lockDiff .String ()),
207+ )
208+ }
163209
164210 if app != nil {
165211 app .HandleEvent (appID , session , event )
0 commit comments