This is an automated email from the ASF dual-hosted git repository.

sruehl pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git


The following commit(s) were added to refs/heads/develop by this push:
     new 1b612551f7 fix(plc4go): stop leaking codec workers on cached reconnect
1b612551f7 is described below

commit 1b612551f7266f9e505b1c609648546190303207
Author: Sebastian Rühl <[email protected]>
AuthorDate: Wed Jun 17 16:44:12 2026 +0200

    fix(plc4go): stop leaking codec workers on cached reconnect
    
    The connection cache's returnConnection() reconnected an invalidated 
connection via connect(), which overwrites c.connection with a freshly 
established one without closing the old one. The stale connection's 
DefaultCodec ReceiveWork/ExpireWork goroutines kept running, leaking a pair per 
invalidate->reconnect cycle. Against an endpoint that accepts TCP but is 
unresponsive at the protocol layer this recurs every poll and grows without 
bound (observed ~40k of each worker on a wedged gateway).
    
     Close the stale connection before reconnecting so the codec is 
disconnected and its workers terminate.
    
     Also add the missing ctx.Done() case to DefaultCodec.ExpireWork's idle 
select. The "no expectations" branch waited only on notifyExpireWorker or a 30s 
timer, so the worker ignored context cancellation there (ReceiveWork and 
ExpireWork's active branch already handle ctx.Done()).
---
 plc4go/pkg/api/cache/connectionContainer.go | 17 +++++++++++++++++
 plc4go/spi/default/DefaultCodec.go          |  3 +++
 2 files changed, 20 insertions(+)

diff --git a/plc4go/pkg/api/cache/connectionContainer.go 
b/plc4go/pkg/api/cache/connectionContainer.go
index 6226e14b2e..2a9009f328 100644
--- a/plc4go/pkg/api/cache/connectionContainer.go
+++ b/plc4go/pkg/api/cache/connectionContainer.go
@@ -196,6 +196,23 @@ func (c *connectionContainer) returnConnection(ctx 
context.Context, newState cac
                        Str("connectionString", c.connectionString).
                        Stringer("newState", newState).
                        Msg("Client returned a connection, reconnecting.")
+               // Close the stale connection before reconnecting. c.connect() 
overwrites
+               // c.connection with a freshly-established one, so without this 
the previous
+               // connection's message-codec workers (ReceiveWork/ExpireWork) 
keep running,
+               // leaking a pair of goroutines on every invalidate->reconnect 
cycle. Against
+               // an endpoint that accepts TCP but is unresponsive at the 
protocol layer this
+               // recurs every poll and grows without bound.
+               c.lock.Lock()
+               stale := c.connection
+               c.connection = nil
+               c.lock.Unlock()
+               if stale != nil {
+                       if err := stale.Close(); err != nil {
+                               c.log.Debug().Err(err).
+                                       Str("connectionString", 
c.connectionString).
+                                       Msg("Error closing stale connection 
before reconnect")
+                       }
+               }
                c.connect(ctx)
        default:
                c.log.Debug().Str("connectionString", 
c.connectionString).Msg("Client returned valid connection.")
diff --git a/plc4go/spi/default/DefaultCodec.go 
b/plc4go/spi/default/DefaultCodec.go
index 729dc3d5a2..aa61c14fc8 100644
--- a/plc4go/spi/default/DefaultCodec.go
+++ b/plc4go/spi/default/DefaultCodec.go
@@ -374,6 +374,9 @@ mainLoop:
                        select {
                        case <-m.notifyExpireWorker:
                                workerLog.Trace().Msg("waking up because of 
notification")
+                       case <-m.ctx.Done():
+                               workerLog.Trace().Msg("context done, exiting 
expire work")
+                               return
                        case <-timer.C:
                                workerLog.Trace().Msg("waking up for next 
expire")
                        }

Reply via email to