@@ -121,7 +121,6 @@ func main() {
121121 stz ,
122122 nekoAuthClient ,
123123 captureSession ,
124- storageWriter ,
125124 config .DisplayNum ,
126125 )
127126 if err != nil {
@@ -256,12 +255,19 @@ func main() {
256255 }
257256 }()
258257
258+ // Give the storage writer its own cancellable context so we can stop it
259+ // independently of the signal context. This lets us call captureSession.Stop()
260+ // (which publishes SessionEnded to the ring) before cancelling the writer,
261+ // ensuring the writer can process and flush SessionEnded to S2.
262+ writerCtx , writerCancel := context .WithCancel (context .Background ())
263+ defer writerCancel ()
264+
259265 // Start the S2 storage writer goroutine (no-op if S2 not configured).
260266 storageDone := make (chan struct {})
261267 if storageWriter != nil {
262268 go func () {
263269 defer close (storageDone )
264- storageWriter .Run (ctx )
270+ storageWriter .Run (writerCtx )
265271 }()
266272 } else {
267273 close (storageDone )
@@ -274,6 +280,14 @@ func main() {
274280 shutdownCtx , shutdownCancel := context .WithTimeout (context .Background (), 10 * time .Second )
275281 defer shutdownCancel ()
276282
283+ // Stop the active capture session now, while the storage writer is still
284+ // alive (writerCtx not yet cancelled), so the writer can process the
285+ // SessionEnded event and flush it to S2 before tearing down.
286+ captureSession .Stop ()
287+
288+ // Cancel the writer so it exits after draining remaining events.
289+ writerCancel ()
290+
277291 // Drain storage writer, close it (bounded by shutdownCtx), and shut down
278292 // all HTTP servers in parallel so the full 10s budget is available to each.
279293 g , _ := errgroup .WithContext (shutdownCtx )
0 commit comments