From 8f6d3a0d99e60414f45f6b3d18e7ee70f8d952ff Mon Sep 17 00:00:00 2001 From: Mikayla Toffler Date: Tue, 10 Dec 2024 16:45:28 -0500 Subject: [PATCH 01/17] implement stopchan on contribroutines --- contrib/database/sql/metrics.go | 33 +++++++++++++-------- ddtrace/tracer/tracer.go | 2 ++ internal/contribroutines/contribroutines.go | 11 +++++++ 3 files changed, 34 insertions(+), 12 deletions(-) create mode 100644 internal/contribroutines/contribroutines.go diff --git a/contrib/database/sql/metrics.go b/contrib/database/sql/metrics.go index d8ff4ed266..10a4b18566 100644 --- a/contrib/database/sql/metrics.go +++ b/contrib/database/sql/metrics.go @@ -10,6 +10,7 @@ import ( "time" "gopkg.in/DataDog/dd-trace-go.v1/internal" + "gopkg.in/DataDog/dd-trace-go.v1/internal/contribroutines" "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" ) @@ -35,18 +36,26 @@ var interval = 10 * time.Second // the caller should always ensure that db & statsd are non-nil func pollDBStats(statsd internal.StatsdClient, db *sql.DB) { log.Debug("DB stats will be gathered and sent every %v.", interval) - for range time.NewTicker(interval).C { - log.Debug("Reporting DB.Stats metrics...") - stat := db.Stats() - statsd.Gauge(MaxOpenConnections, float64(stat.MaxOpenConnections), []string{}, 1) - statsd.Gauge(OpenConnections, float64(stat.OpenConnections), []string{}, 1) - statsd.Gauge(InUse, float64(stat.InUse), []string{}, 1) - statsd.Gauge(Idle, float64(stat.Idle), []string{}, 1) - statsd.Gauge(WaitCount, float64(stat.WaitCount), []string{}, 1) - statsd.Timing(WaitDuration, stat.WaitDuration, []string{}, 1) - statsd.Gauge(MaxIdleClosed, float64(stat.MaxIdleClosed), []string{}, 1) - statsd.Gauge(MaxIdleTimeClosed, float64(stat.MaxIdleTimeClosed), []string{}, 1) - statsd.Gauge(MaxLifetimeClosed, float64(stat.MaxLifetimeClosed), []string{}, 1) + stop := contribroutines.GetStopChan() + ticker := time.NewTicker(interval) + defer ticker.Stop() + for { + select { + case <-ticker.C: + log.Debug("Reporting DB.Stats metrics...") + stat := db.Stats() + statsd.Gauge(MaxOpenConnections, float64(stat.MaxOpenConnections), []string{}, 1) + statsd.Gauge(OpenConnections, float64(stat.OpenConnections), []string{}, 1) + statsd.Gauge(InUse, float64(stat.InUse), []string{}, 1) + statsd.Gauge(Idle, float64(stat.Idle), []string{}, 1) + statsd.Gauge(WaitCount, float64(stat.WaitCount), []string{}, 1) + statsd.Timing(WaitDuration, stat.WaitDuration, []string{}, 1) + statsd.Gauge(MaxIdleClosed, float64(stat.MaxIdleClosed), []string{}, 1) + statsd.Gauge(MaxIdleTimeClosed, float64(stat.MaxIdleTimeClosed), []string{}, 1) + statsd.Gauge(MaxLifetimeClosed, float64(stat.MaxLifetimeClosed), []string{}, 1) + case <-stop: + return + } } } diff --git a/ddtrace/tracer/tracer.go b/ddtrace/tracer/tracer.go index 89277045de..ad9263be32 100644 --- a/ddtrace/tracer/tracer.go +++ b/ddtrace/tracer/tracer.go @@ -24,6 +24,7 @@ import ( globalinternal "gopkg.in/DataDog/dd-trace-go.v1/internal" "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec" appsecConfig "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/config" + "gopkg.in/DataDog/dd-trace-go.v1/internal/contribroutines" "gopkg.in/DataDog/dd-trace-go.v1/internal/datastreams" "gopkg.in/DataDog/dd-trace-go.v1/internal/hostname" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" @@ -710,6 +711,7 @@ func (t *tracer) Stop() { if t.logFile != nil { t.logFile.Close() } + contribroutines.Stop() } // Inject uses the configured or default TextMap Propagator. diff --git a/internal/contribroutines/contribroutines.go b/internal/contribroutines/contribroutines.go new file mode 100644 index 0000000000..423718e23c --- /dev/null +++ b/internal/contribroutines/contribroutines.go @@ -0,0 +1,11 @@ +package contribroutines + +var stop chan struct{} + +func Stop() { + close(stop) +} + +func GetStopChan() chan struct{} { + return stop +} From c7f7fd3e7195bdb33c5820092c2041ac7219d72d Mon Sep 17 00:00:00 2001 From: Mikayla Toffler Date: Tue, 10 Dec 2024 17:00:12 -0500 Subject: [PATCH 02/17] initialize channel at package root --- internal/contribroutines/contribroutines.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/internal/contribroutines/contribroutines.go b/internal/contribroutines/contribroutines.go index 423718e23c..90da259055 100644 --- a/internal/contribroutines/contribroutines.go +++ b/internal/contribroutines/contribroutines.go @@ -1,11 +1,24 @@ package contribroutines +import "sync" + var stop chan struct{} +var once sync.Once func Stop() { - close(stop) + once.Do(func() { + if stop == nil { + stop = make(chan struct{}) + } + close(stop) + }) } func GetStopChan() chan struct{} { + once.Do(func() { + if stop == nil { + stop = make(chan struct{}) + } + }) return stop } From 1b8bb5f92ae9f7faa7f7f3a8b58a9af2b9de02dc Mon Sep 17 00:00:00 2001 From: Mikayla Toffler Date: Tue, 10 Dec 2024 17:00:30 -0500 Subject: [PATCH 03/17] initialize stop channel at package root --- internal/contribroutines/contribroutines.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/internal/contribroutines/contribroutines.go b/internal/contribroutines/contribroutines.go index 90da259055..c1c0c256b1 100644 --- a/internal/contribroutines/contribroutines.go +++ b/internal/contribroutines/contribroutines.go @@ -2,23 +2,20 @@ package contribroutines import "sync" -var stop chan struct{} +var stop chan struct{} = make(chan struct{}) var once sync.Once func Stop() { once.Do(func() { - if stop == nil { - stop = make(chan struct{}) - } close(stop) }) } func GetStopChan() chan struct{} { - once.Do(func() { - if stop == nil { - stop = make(chan struct{}) - } - }) + // once.Do(func() { + // if stop == nil { + // stop = make(chan struct{}) + // } + // }) return stop } From f0e844b4fa9e664eef1adf0d1487ab165d510345 Mon Sep 17 00:00:00 2001 From: Mikayla Toffler Date: Tue, 10 Dec 2024 17:00:41 -0500 Subject: [PATCH 04/17] remove comment --- internal/contribroutines/contribroutines.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/internal/contribroutines/contribroutines.go b/internal/contribroutines/contribroutines.go index c1c0c256b1..c94c879f46 100644 --- a/internal/contribroutines/contribroutines.go +++ b/internal/contribroutines/contribroutines.go @@ -12,10 +12,5 @@ func Stop() { } func GetStopChan() chan struct{} { - // once.Do(func() { - // if stop == nil { - // stop = make(chan struct{}) - // } - // }) return stop } From f0f73afb2ec069540516a6be6cc2012ff8e8c0fa Mon Sep 17 00:00:00 2001 From: Mikayla Toffler Date: Tue, 10 Dec 2024 17:06:45 -0500 Subject: [PATCH 05/17] copywrite header --- internal/contribroutines/contribroutines.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/contribroutines/contribroutines.go b/internal/contribroutines/contribroutines.go index c94c879f46..e352346de7 100644 --- a/internal/contribroutines/contribroutines.go +++ b/internal/contribroutines/contribroutines.go @@ -1,3 +1,7 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016 Datadog, Inc. package contribroutines import "sync" From bf4f376cc2cc48d92644de4221bddde05a479115 Mon Sep 17 00:00:00 2001 From: Mikayla Toffler Date: Wed, 11 Dec 2024 09:21:58 -0500 Subject: [PATCH 06/17] Cleanup var declaration --- internal/contribroutines/contribroutines.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/contribroutines/contribroutines.go b/internal/contribroutines/contribroutines.go index e352346de7..9667c2e37b 100644 --- a/internal/contribroutines/contribroutines.go +++ b/internal/contribroutines/contribroutines.go @@ -6,8 +6,10 @@ package contribroutines import "sync" -var stop chan struct{} = make(chan struct{}) -var once sync.Once +var ( + stop chan struct{} = make(chan struct{}) + once sync.Once +) func Stop() { once.Do(func() { From 9e86accc1f9a5e4d531c76645888ed800c9289bd Mon Sep 17 00:00:00 2001 From: Mikayla Toffler Date: Fri, 13 Dec 2024 16:16:29 -0500 Subject: [PATCH 07/17] lock access to prevent race condition --- internal/contribroutines/contribroutines.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/contribroutines/contribroutines.go b/internal/contribroutines/contribroutines.go index 9667c2e37b..b2a7282883 100644 --- a/internal/contribroutines/contribroutines.go +++ b/internal/contribroutines/contribroutines.go @@ -9,14 +9,19 @@ import "sync" var ( stop chan struct{} = make(chan struct{}) once sync.Once + mu sync.Mutex ) func Stop() { once.Do(func() { + mu.Lock() + defer mu.Unlock() close(stop) }) } func GetStopChan() chan struct{} { + mu.Lock() + defer mu.Unlock() return stop } From 12241c9e58c35ba28a5a6de933addd6eebd5bc4c Mon Sep 17 00:00:00 2001 From: Mikayla Toffler Date: Mon, 16 Dec 2024 10:46:05 -0500 Subject: [PATCH 08/17] added race detection tests to contribroutines_test.go --- .../contribroutines/contribroutines_test.go | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 internal/contribroutines/contribroutines_test.go diff --git a/internal/contribroutines/contribroutines_test.go b/internal/contribroutines/contribroutines_test.go new file mode 100644 index 0000000000..c54aae09e2 --- /dev/null +++ b/internal/contribroutines/contribroutines_test.go @@ -0,0 +1,74 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016 Datadog, Inc. +package contribroutines + +import ( + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestContribRoutines(t *testing.T) { + var wg sync.WaitGroup + wg.Add(1) + var done bool + go func() { + doSomething(&wg, &done, GetStopChan()) + }() + Stop() + wg.Wait() + assert.True(t, done) +} + +func doSomething(wg *sync.WaitGroup, done *bool, stop chan struct{}) { + defer wg.Done() + ticker := time.NewTicker(500 * time.Millisecond) + defer ticker.Stop() + for { + select { + case <-ticker.C: + case <-stop: + *done = true + return + } + } +} + +func TestStopConcurrency(t *testing.T) { + var wg sync.WaitGroup + + for i := 0; i < 100; i++ { + wg.Add(1) + go func() { + defer wg.Done() + Stop() + }() + } + + wg.Wait() + + select { + case <-stop: + // channel is closed, so Stop() was called successfully + case <-time.After(1 * time.Second): + t.Error("stop channel was not closed within 1 second") + } +} + +func TestGetStopChanConcurrency(t *testing.T) { + var wg sync.WaitGroup + + for i := 0; i < 100; i++ { + wg.Add(1) + go func() { + defer wg.Done() + GetStopChan() + }() + } + + wg.Wait() +} From 41c3ad9398293582c73e68348c7170b488966fc5 Mon Sep 17 00:00:00 2001 From: Mikayla Toffler Date: Mon, 16 Dec 2024 11:00:44 -0500 Subject: [PATCH 09/17] move stop chan initialization out of pollDBStats --- contrib/database/sql/metrics.go | 4 +--- contrib/database/sql/sql.go | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/contrib/database/sql/metrics.go b/contrib/database/sql/metrics.go index 10a4b18566..5d662ec9c6 100644 --- a/contrib/database/sql/metrics.go +++ b/contrib/database/sql/metrics.go @@ -10,7 +10,6 @@ import ( "time" "gopkg.in/DataDog/dd-trace-go.v1/internal" - "gopkg.in/DataDog/dd-trace-go.v1/internal/contribroutines" "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" ) @@ -34,9 +33,8 @@ var interval = 10 * time.Second // pollDBStats calls (*DB).Stats on the db at a predetermined interval. It pushes the DBStats off to the statsd client. // the caller should always ensure that db & statsd are non-nil -func pollDBStats(statsd internal.StatsdClient, db *sql.DB) { +func pollDBStats(statsd internal.StatsdClient, db *sql.DB, stop chan struct{}) { log.Debug("DB stats will be gathered and sent every %v.", interval) - stop := contribroutines.GetStopChan() ticker := time.NewTicker(interval) defer ticker.Stop() for { diff --git a/contrib/database/sql/sql.go b/contrib/database/sql/sql.go index b26318d0d3..01c0459ada 100644 --- a/contrib/database/sql/sql.go +++ b/contrib/database/sql/sql.go @@ -25,6 +25,7 @@ import ( sqlinternal "gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql/internal" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" + "gopkg.in/DataDog/dd-trace-go.v1/internal/contribroutines" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" "gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry" ) @@ -211,7 +212,7 @@ func OpenDB(c driver.Connector, opts ...Option) *sql.DB { } db := sql.OpenDB(tc) if cfg.dbStats && cfg.statsdClient != nil { - go pollDBStats(cfg.statsdClient, db) + go pollDBStats(cfg.statsdClient, db, contribroutines.GetStopChan()) } return db } From 2cb3519ecea5b13f9124cefad81b3312b1abf86b Mon Sep 17 00:00:00 2001 From: Mikayla Toffler Date: Mon, 16 Dec 2024 14:50:51 -0500 Subject: [PATCH 10/17] Apply stopchan to pgx pollPoolStats --- contrib/jackc/pgx.v5/metrics.go | 39 +++++++++++++++++++-------------- contrib/jackc/pgx.v5/pgxpool.go | 3 ++- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/contrib/jackc/pgx.v5/metrics.go b/contrib/jackc/pgx.v5/metrics.go index eb94c50bfc..2bcf7c83b3 100644 --- a/contrib/jackc/pgx.v5/metrics.go +++ b/contrib/jackc/pgx.v5/metrics.go @@ -34,23 +34,30 @@ const ( var interval = 10 * time.Second // pollPoolStats calls (*pgxpool).Stats on the pool at a predetermined interval. It pushes the pool Stats off to the statsd client. -func pollPoolStats(statsd internal.StatsdClient, pool *pgxpool.Pool) { +func pollPoolStats(statsd internal.StatsdClient, pool *pgxpool.Pool, stop chan struct{}) { log.Debug("contrib/jackc/pgx.v5: Traced pool connection found: Pool stats will be gathered and sent every %v.", interval) - for range time.NewTicker(interval).C { - log.Debug("contrib/jackc/pgx.v5: Reporting pgxpool.Stat metrics...") - stat := pool.Stat() - statsd.Gauge(AcquireCount, float64(stat.AcquireCount()), []string{}, 1) - statsd.Timing(AcquireDuration, stat.AcquireDuration(), []string{}, 1) - statsd.Gauge(AcquiredConns, float64(stat.AcquiredConns()), []string{}, 1) - statsd.Gauge(CanceledAcquireCount, float64(stat.CanceledAcquireCount()), []string{}, 1) - statsd.Gauge(ConstructingConns, float64(stat.ConstructingConns()), []string{}, 1) - statsd.Gauge(EmptyAcquireCount, float64(stat.EmptyAcquireCount()), []string{}, 1) - statsd.Gauge(IdleConns, float64(stat.IdleConns()), []string{}, 1) - statsd.Gauge(MaxConns, float64(stat.MaxConns()), []string{}, 1) - statsd.Gauge(TotalConns, float64(stat.TotalConns()), []string{}, 1) - statsd.Gauge(NewConnsCount, float64(stat.NewConnsCount()), []string{}, 1) - statsd.Gauge(MaxLifetimeDestroyCount, float64(stat.MaxLifetimeDestroyCount()), []string{}, 1) - statsd.Gauge(MaxIdleDestroyCount, float64(stat.MaxIdleDestroyCount()), []string{}, 1) + ticker := time.NewTicker(interval) + defer ticker.Stop() + for { + select { + case <-ticker.C: + log.Debug("contrib/jackc/pgx.v5: Reporting pgxpool.Stat metrics...") + stat := pool.Stat() + statsd.Gauge(AcquireCount, float64(stat.AcquireCount()), []string{}, 1) + statsd.Timing(AcquireDuration, stat.AcquireDuration(), []string{}, 1) + statsd.Gauge(AcquiredConns, float64(stat.AcquiredConns()), []string{}, 1) + statsd.Gauge(CanceledAcquireCount, float64(stat.CanceledAcquireCount()), []string{}, 1) + statsd.Gauge(ConstructingConns, float64(stat.ConstructingConns()), []string{}, 1) + statsd.Gauge(EmptyAcquireCount, float64(stat.EmptyAcquireCount()), []string{}, 1) + statsd.Gauge(IdleConns, float64(stat.IdleConns()), []string{}, 1) + statsd.Gauge(MaxConns, float64(stat.MaxConns()), []string{}, 1) + statsd.Gauge(TotalConns, float64(stat.TotalConns()), []string{}, 1) + statsd.Gauge(NewConnsCount, float64(stat.NewConnsCount()), []string{}, 1) + statsd.Gauge(MaxLifetimeDestroyCount, float64(stat.MaxLifetimeDestroyCount()), []string{}, 1) + statsd.Gauge(MaxIdleDestroyCount, float64(stat.MaxIdleDestroyCount()), []string{}, 1) + case <-stop: + return + } } } diff --git a/contrib/jackc/pgx.v5/pgxpool.go b/contrib/jackc/pgx.v5/pgxpool.go index 11d93b0859..7f2b677305 100644 --- a/contrib/jackc/pgx.v5/pgxpool.go +++ b/contrib/jackc/pgx.v5/pgxpool.go @@ -9,6 +9,7 @@ import ( "context" "github.com/jackc/pgx/v5/pgxpool" + "gopkg.in/DataDog/dd-trace-go.v1/internal/contribroutines" ) func NewPool(ctx context.Context, connString string, opts ...Option) (*pgxpool.Pool, error) { @@ -30,7 +31,7 @@ func NewPoolWithConfig(ctx context.Context, config *pgxpool.Config, opts ...Opti return nil, err } if tracer.cfg.poolStats && tracer.cfg.statsdClient != nil { - go pollPoolStats(tracer.cfg.statsdClient, pool) + go pollPoolStats(tracer.cfg.statsdClient, pool, contribroutines.GetStopChan()) } return pool, nil } From 37b52f94c7f44bd4ae8c9836114759a3e234f7f4 Mon Sep 17 00:00:00 2001 From: Mikayla Toffler Date: Mon, 16 Dec 2024 16:10:38 -0500 Subject: [PATCH 11/17] move lock outside of once.Do --- internal/contribroutines/contribroutines.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/contribroutines/contribroutines.go b/internal/contribroutines/contribroutines.go index b2a7282883..2b9e6352c0 100644 --- a/internal/contribroutines/contribroutines.go +++ b/internal/contribroutines/contribroutines.go @@ -13,9 +13,9 @@ var ( ) func Stop() { + mu.Lock() + defer mu.Unlock() once.Do(func() { - mu.Lock() - defer mu.Unlock() close(stop) }) } From 11f13fa307f23830bce35859cbb21cdad771d76d Mon Sep 17 00:00:00 2001 From: Mikayla Toffler Date: Mon, 13 Jan 2025 15:49:05 -0500 Subject: [PATCH 12/17] Hook pollDBStats stop condition into db.Close() invocation --- contrib/database/sql/metrics.go | 21 +++++++++++++++++-- contrib/database/sql/metrics_test.go | 30 ++++++++++++++++++++++++++++ contrib/database/sql/sql.go | 7 +++++++ contrib/database/sql/sql_test.go | 24 ++++++++++++++++++++++ contrib/net/http/trace_test.go | 4 ++-- 5 files changed, 82 insertions(+), 4 deletions(-) diff --git a/contrib/database/sql/metrics.go b/contrib/database/sql/metrics.go index 5d662ec9c6..5b1450db3b 100644 --- a/contrib/database/sql/metrics.go +++ b/contrib/database/sql/metrics.go @@ -7,6 +7,7 @@ package sql // import "gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql" import ( "database/sql" + "sync" "time" "gopkg.in/DataDog/dd-trace-go.v1/internal" @@ -33,7 +34,7 @@ var interval = 10 * time.Second // pollDBStats calls (*DB).Stats on the db at a predetermined interval. It pushes the DBStats off to the statsd client. // the caller should always ensure that db & statsd are non-nil -func pollDBStats(statsd internal.StatsdClient, db *sql.DB, stop chan struct{}) { +func pollDBStats(statsd internal.StatsdClient, db *sql.DB, tracerStop chan struct{}) { log.Debug("DB stats will be gathered and sent every %v.", interval) ticker := time.NewTicker(interval) defer ticker.Stop() @@ -51,7 +52,9 @@ func pollDBStats(statsd internal.StatsdClient, db *sql.DB, stop chan struct{}) { statsd.Gauge(MaxIdleClosed, float64(stat.MaxIdleClosed), []string{}, 1) statsd.Gauge(MaxIdleTimeClosed, float64(stat.MaxIdleTimeClosed), []string{}, 1) statsd.Gauge(MaxLifetimeClosed, float64(stat.MaxLifetimeClosed), []string{}, 1) - case <-stop: + case <-tracerStop: + return + case <-dbClose: return } } @@ -70,3 +73,17 @@ func statsTags(c *config) []string { } return tags } + +var ( + dbClose chan struct{} = make(chan struct{}) + once sync.Once + mu sync.Mutex +) + +func Close() { + mu.Lock() + defer mu.Unlock() + once.Do(func() { + close(dbClose) + }) +} diff --git a/contrib/database/sql/metrics_test.go b/contrib/database/sql/metrics_test.go index e68e968229..f7d516057d 100644 --- a/contrib/database/sql/metrics_test.go +++ b/contrib/database/sql/metrics_test.go @@ -6,9 +6,12 @@ package sql import ( + "sync" "testing" + "github.com/DataDog/datadog-go/v5/statsd" "github.com/stretchr/testify/assert" + "gopkg.in/DataDog/dd-trace-go.v1/internal/contribroutines" "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig" ) @@ -64,3 +67,30 @@ func TestStatsTags(t *testing.T) { }) resetGlobalConfig() } + +func TestPollDBStats(t *testing.T) { + db := setupPostgres(t) + tracerStop := contribroutines.GetStopChan() + t.Run("tracerStop", func(t *testing.T) { + var wg sync.WaitGroup + + wg.Add(1) + go func() { + defer wg.Done() + pollDBStats(&statsd.NoOpClientDirect{}, db, tracerStop) + }() + contribroutines.Stop() + wg.Wait() + }) + t.Run("dbStop", func(t *testing.T) { + var wg sync.WaitGroup + + wg.Add(1) + go func() { + defer wg.Done() + pollDBStats(&statsd.NoOpClientDirect{}, db, tracerStop) + }() + db.Close() + wg.Wait() + }) +} diff --git a/contrib/database/sql/sql.go b/contrib/database/sql/sql.go index 01c0459ada..333bbd18fc 100644 --- a/contrib/database/sql/sql.go +++ b/contrib/database/sql/sql.go @@ -172,6 +172,13 @@ func (t *tracedConnector) Driver() driver.Driver { return t.connector.Driver() } +// Close sends a signal on any goroutines that rely on an open DB to stop. +// This method will be invoked when DB.Close() is called: https://cs.opensource.google/go/go/+/refs/tags/go1.23.4:src/database/sql/sql.go;l=943-947 +func (t *tracedConnector) Close() error { + Close() + return nil +} + // from Go stdlib implementation of sql.Open type dsnConnector struct { dsn string diff --git a/contrib/database/sql/sql_test.go b/contrib/database/sql/sql_test.go index 5b50b7effc..e4655cc112 100644 --- a/contrib/database/sql/sql_test.go +++ b/contrib/database/sql/sql_test.go @@ -7,6 +7,7 @@ package sql import ( "context" + "database/sql" "database/sql/driver" "errors" "fmt" @@ -529,3 +530,26 @@ func TestNamingSchema(t *testing.T) { t.Run("SpanName", namingschematest.NewSpanNameTest(genSpans, assertOpV0, assertOpV1)) }) } + +func TestDBClose(t *testing.T) { + db := setupPostgres(t) + + // assert that "close" channel is closed on the call to db.Close() + var wg sync.WaitGroup + wg.Add(1) + go func() { + <-dbClose + wg.Done() + }() + db.Close() + wg.Wait() +} + +func setupPostgres(t *testing.T) *sql.DB { + driverName := "postgres" + Register(driverName, &pq.Driver{}) + defer unregister(driverName) + db, err := Open(driverName, "postgres://postgres:postgres@127.0.0.1:5432/postgres?sslmode=disable") + require.NoError(t, err) + return db +} diff --git a/contrib/net/http/trace_test.go b/contrib/net/http/trace_test.go index f85a592861..0489f8c3ef 100644 --- a/contrib/net/http/trace_test.go +++ b/contrib/net/http/trace_test.go @@ -329,8 +329,8 @@ func TestTraceAndServe(t *testing.T) { t.Setenv("DD_TRACE_HTTP_SERVER_ERROR_STATUSES", "500") cfg := &ServeConfig{ - Service: "service", - Resource: "resource", + Service: "service", + Resource: "resource", } handler := func(w http.ResponseWriter, r *http.Request) { From 42935d672b089045fe18f4d7e5b45a03e2bc9e93 Mon Sep 17 00:00:00 2001 From: Mikayla Toffler Date: Mon, 13 Jan 2025 16:05:55 -0500 Subject: [PATCH 13/17] nits --- contrib/database/sql/metrics.go | 12 ++++++------ contrib/database/sql/sql.go | 2 +- contrib/database/sql/sql_test.go | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/contrib/database/sql/metrics.go b/contrib/database/sql/metrics.go index 5b1450db3b..17293eb1c1 100644 --- a/contrib/database/sql/metrics.go +++ b/contrib/database/sql/metrics.go @@ -54,7 +54,7 @@ func pollDBStats(statsd internal.StatsdClient, db *sql.DB, tracerStop chan struc statsd.Gauge(MaxLifetimeClosed, float64(stat.MaxLifetimeClosed), []string{}, 1) case <-tracerStop: return - case <-dbClose: + case <-dbStop: return } } @@ -75,15 +75,15 @@ func statsTags(c *config) []string { } var ( - dbClose chan struct{} = make(chan struct{}) - once sync.Once - mu sync.Mutex + dbStop chan struct{} = make(chan struct{}) + once sync.Once + mu sync.Mutex ) -func Close() { +func dbClose() { mu.Lock() defer mu.Unlock() once.Do(func() { - close(dbClose) + close(dbStop) }) } diff --git a/contrib/database/sql/sql.go b/contrib/database/sql/sql.go index 333bbd18fc..898afc22c8 100644 --- a/contrib/database/sql/sql.go +++ b/contrib/database/sql/sql.go @@ -175,7 +175,7 @@ func (t *tracedConnector) Driver() driver.Driver { // Close sends a signal on any goroutines that rely on an open DB to stop. // This method will be invoked when DB.Close() is called: https://cs.opensource.google/go/go/+/refs/tags/go1.23.4:src/database/sql/sql.go;l=943-947 func (t *tracedConnector) Close() error { - Close() + dbClose() return nil } diff --git a/contrib/database/sql/sql_test.go b/contrib/database/sql/sql_test.go index e4655cc112..58e82dde3d 100644 --- a/contrib/database/sql/sql_test.go +++ b/contrib/database/sql/sql_test.go @@ -534,11 +534,11 @@ func TestNamingSchema(t *testing.T) { func TestDBClose(t *testing.T) { db := setupPostgres(t) - // assert that "close" channel is closed on the call to db.Close() + // assert that dbStop channel is closed on the call to db.Close() var wg sync.WaitGroup wg.Add(1) go func() { - <-dbClose + <-dbStop wg.Done() }() db.Close() From 3fc11213a10083958eb53231dfc3680979e388d0 Mon Sep 17 00:00:00 2001 From: Mikayla Toffler Date: Mon, 13 Jan 2025 17:41:22 -0500 Subject: [PATCH 14/17] make tracer initialize new contribroutines.stop channel on startup --- ddtrace/tracer/tracer.go | 1 + ddtrace/tracer/tracer_test.go | 22 +++++++++++++++++++++ internal/contribroutines/contribroutines.go | 9 ++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/ddtrace/tracer/tracer.go b/ddtrace/tracer/tracer.go index 26ecc88331..5cf6cb231e 100644 --- a/ddtrace/tracer/tracer.go +++ b/ddtrace/tracer/tracer.go @@ -204,6 +204,7 @@ func Start(opts ...StartOption) { } _ = t.hostname() // Prime the hostname cache + contribroutines.InitStopChan() } // Stop stops the started tracer. Subsequent calls are valid but become no-op. diff --git a/ddtrace/tracer/tracer_test.go b/ddtrace/tracer/tracer_test.go index dd1820621e..5d62572a6c 100644 --- a/ddtrace/tracer/tracer_test.go +++ b/ddtrace/tracer/tracer_test.go @@ -30,6 +30,7 @@ import ( "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal" maininternal "gopkg.in/DataDog/dd-trace-go.v1/internal" + "gopkg.in/DataDog/dd-trace-go.v1/internal/contribroutines" "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" "gopkg.in/DataDog/dd-trace-go.v1/internal/statsdtest" @@ -255,6 +256,27 @@ func TestTracerStart(t *testing.T) { tr.Stop() tr.Stop() }) + t.Run("contribroutines", func(t *testing.T) { + // assert tracer.Start initializes contribroutines.stop + Start() + s1 := contribroutines.GetStopChan() + assert.NotNil(t, s1) + + // assert tracer.Stop closes the channel + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + <-s1 + }() + Stop() + wg.Wait() + + // assert tracer.Start initializes contribroutines.stop to a new channel every time + Start() + s2 := contribroutines.GetStopChan() + assert.NotEqual(t, s1, s2) + }) } func TestTracerLogFile(t *testing.T) { diff --git a/internal/contribroutines/contribroutines.go b/internal/contribroutines/contribroutines.go index 2b9e6352c0..eb977a463c 100644 --- a/internal/contribroutines/contribroutines.go +++ b/internal/contribroutines/contribroutines.go @@ -7,14 +7,21 @@ package contribroutines import "sync" var ( - stop chan struct{} = make(chan struct{}) + stop chan struct{} once sync.Once mu sync.Mutex ) +func InitStopChan() { + stop = make(chan struct{}) +} + func Stop() { mu.Lock() defer mu.Unlock() + if stop == nil { + InitStopChan() + } once.Do(func() { close(stop) }) From 5b116f8fa5f5eed8ac70d0e42737e05ec93aa9a8 Mon Sep 17 00:00:00 2001 From: Mikayla Toffler Date: Tue, 14 Jan 2025 10:54:32 -0500 Subject: [PATCH 15/17] Add reminder: implement stop condition on db.Close for pgx --- contrib/jackc/pgx.v5/metrics.go | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/jackc/pgx.v5/metrics.go b/contrib/jackc/pgx.v5/metrics.go index 2bcf7c83b3..e87a710c8f 100644 --- a/contrib/jackc/pgx.v5/metrics.go +++ b/contrib/jackc/pgx.v5/metrics.go @@ -35,6 +35,7 @@ var interval = 10 * time.Second // pollPoolStats calls (*pgxpool).Stats on the pool at a predetermined interval. It pushes the pool Stats off to the statsd client. func pollPoolStats(statsd internal.StatsdClient, pool *pgxpool.Pool, stop chan struct{}) { + // TODO: Create stop condition for pgx on db.Close log.Debug("contrib/jackc/pgx.v5: Traced pool connection found: Pool stats will be gathered and sent every %v.", interval) ticker := time.NewTicker(interval) defer ticker.Stop() From 79ee5f50965f740d53f33df7d965b1c202e92dc7 Mon Sep 17 00:00:00 2001 From: Mikayla Toffler Date: Fri, 17 Jan 2025 12:20:24 -0500 Subject: [PATCH 16/17] revamp: tie pollDBStats to lifetime of db connection, only --- contrib/database/sql/metrics.go | 21 +----- contrib/database/sql/metrics_test.go | 35 +++------ contrib/database/sql/sql.go | 7 +- contrib/database/sql/sql_test.go | 22 ++---- contrib/jackc/pgx.v5/metrics.go | 4 +- contrib/jackc/pgx.v5/pgxpool.go | 3 +- ddtrace/tracer/tracer.go | 3 - ddtrace/tracer/tracer_test.go | 22 ------ internal/contribroutines/contribroutines.go | 34 --------- .../contribroutines/contribroutines_test.go | 74 ------------------- 10 files changed, 25 insertions(+), 200 deletions(-) delete mode 100644 internal/contribroutines/contribroutines.go delete mode 100644 internal/contribroutines/contribroutines_test.go diff --git a/contrib/database/sql/metrics.go b/contrib/database/sql/metrics.go index 17293eb1c1..5d662ec9c6 100644 --- a/contrib/database/sql/metrics.go +++ b/contrib/database/sql/metrics.go @@ -7,7 +7,6 @@ package sql // import "gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql" import ( "database/sql" - "sync" "time" "gopkg.in/DataDog/dd-trace-go.v1/internal" @@ -34,7 +33,7 @@ var interval = 10 * time.Second // pollDBStats calls (*DB).Stats on the db at a predetermined interval. It pushes the DBStats off to the statsd client. // the caller should always ensure that db & statsd are non-nil -func pollDBStats(statsd internal.StatsdClient, db *sql.DB, tracerStop chan struct{}) { +func pollDBStats(statsd internal.StatsdClient, db *sql.DB, stop chan struct{}) { log.Debug("DB stats will be gathered and sent every %v.", interval) ticker := time.NewTicker(interval) defer ticker.Stop() @@ -52,9 +51,7 @@ func pollDBStats(statsd internal.StatsdClient, db *sql.DB, tracerStop chan struc statsd.Gauge(MaxIdleClosed, float64(stat.MaxIdleClosed), []string{}, 1) statsd.Gauge(MaxIdleTimeClosed, float64(stat.MaxIdleTimeClosed), []string{}, 1) statsd.Gauge(MaxLifetimeClosed, float64(stat.MaxLifetimeClosed), []string{}, 1) - case <-tracerStop: - return - case <-dbStop: + case <-stop: return } } @@ -73,17 +70,3 @@ func statsTags(c *config) []string { } return tags } - -var ( - dbStop chan struct{} = make(chan struct{}) - once sync.Once - mu sync.Mutex -) - -func dbClose() { - mu.Lock() - defer mu.Unlock() - once.Do(func() { - close(dbStop) - }) -} diff --git a/contrib/database/sql/metrics_test.go b/contrib/database/sql/metrics_test.go index f7d516057d..42c1438217 100644 --- a/contrib/database/sql/metrics_test.go +++ b/contrib/database/sql/metrics_test.go @@ -11,7 +11,6 @@ import ( "github.com/DataDog/datadog-go/v5/statsd" "github.com/stretchr/testify/assert" - "gopkg.in/DataDog/dd-trace-go.v1/internal/contribroutines" "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig" ) @@ -68,29 +67,15 @@ func TestStatsTags(t *testing.T) { resetGlobalConfig() } -func TestPollDBStats(t *testing.T) { +func TestPollDBStatsStop(t *testing.T) { db := setupPostgres(t) - tracerStop := contribroutines.GetStopChan() - t.Run("tracerStop", func(t *testing.T) { - var wg sync.WaitGroup - - wg.Add(1) - go func() { - defer wg.Done() - pollDBStats(&statsd.NoOpClientDirect{}, db, tracerStop) - }() - contribroutines.Stop() - wg.Wait() - }) - t.Run("dbStop", func(t *testing.T) { - var wg sync.WaitGroup - - wg.Add(1) - go func() { - defer wg.Done() - pollDBStats(&statsd.NoOpClientDirect{}, db, tracerStop) - }() - db.Close() - wg.Wait() - }) + var wg sync.WaitGroup + stop := make(chan struct{}) + wg.Add(1) + go func() { + defer wg.Done() + pollDBStats(&statsd.NoOpClientDirect{}, db, stop) + }() + close(stop) + wg.Wait() } diff --git a/contrib/database/sql/sql.go b/contrib/database/sql/sql.go index 898afc22c8..4d1398c955 100644 --- a/contrib/database/sql/sql.go +++ b/contrib/database/sql/sql.go @@ -25,7 +25,6 @@ import ( sqlinternal "gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql/internal" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" - "gopkg.in/DataDog/dd-trace-go.v1/internal/contribroutines" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" "gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry" ) @@ -140,6 +139,7 @@ type tracedConnector struct { connector driver.Connector driverName string cfg *config + dbClose chan struct{} } func (t *tracedConnector) Connect(ctx context.Context) (driver.Conn, error) { @@ -175,7 +175,7 @@ func (t *tracedConnector) Driver() driver.Driver { // Close sends a signal on any goroutines that rely on an open DB to stop. // This method will be invoked when DB.Close() is called: https://cs.opensource.google/go/go/+/refs/tags/go1.23.4:src/database/sql/sql.go;l=943-947 func (t *tracedConnector) Close() error { - dbClose() + close(t.dbClose) return nil } @@ -216,10 +216,11 @@ func OpenDB(c driver.Connector, opts ...Option) *sql.DB { connector: c, driverName: driverName, cfg: cfg, + dbClose: make(chan struct{}), } db := sql.OpenDB(tc) if cfg.dbStats && cfg.statsdClient != nil { - go pollDBStats(cfg.statsdClient, db, contribroutines.GetStopChan()) + go pollDBStats(cfg.statsdClient, db, tc.dbClose) } return db } diff --git a/contrib/database/sql/sql_test.go b/contrib/database/sql/sql_test.go index 58e82dde3d..651525fadd 100644 --- a/contrib/database/sql/sql_test.go +++ b/contrib/database/sql/sql_test.go @@ -282,12 +282,13 @@ func TestOpenOptions(t *testing.T) { var tg statsdtest.TestStatsdClient Register(driverName, &pq.Driver{}) defer unregister(driverName) - _, err := Open(driverName, dsn, withStatsdClient(&tg), WithDBStats()) + db, err := Open(driverName, dsn, withStatsdClient(&tg), WithDBStats()) require.NoError(t, err) // The polling interval has been reduced to 500ms for the sake of this test, so at least one round of `pollDBStats` should be complete in 1s deadline := time.Now().Add(1 * time.Second) wantStats := []string{MaxOpenConnections, OpenConnections, InUse, Idle, WaitCount, WaitDuration, MaxIdleClosed, MaxIdleTimeClosed, MaxLifetimeClosed} + var calls1 []string for { if time.Now().After(deadline) { t.Fatalf("Stats not collected in expected interval of %v", interval) @@ -301,11 +302,16 @@ func TestOpenOptions(t *testing.T) { } } // all expected stats have been collected; exit out of loop, test should pass + calls1 = calls break } // not all stats have been collected yet, try again in 50ms time.Sleep(50 * time.Millisecond) } + // Close DB and assert the no further stats have been collected; db.Close should stop the pollDBStats goroutine. + db.Close() + time.Sleep(50 * time.Millisecond) + assert.Equal(t, calls1, tg.CallNames()) }) } @@ -531,20 +537,6 @@ func TestNamingSchema(t *testing.T) { }) } -func TestDBClose(t *testing.T) { - db := setupPostgres(t) - - // assert that dbStop channel is closed on the call to db.Close() - var wg sync.WaitGroup - wg.Add(1) - go func() { - <-dbStop - wg.Done() - }() - db.Close() - wg.Wait() -} - func setupPostgres(t *testing.T) *sql.DB { driverName := "postgres" Register(driverName, &pq.Driver{}) diff --git a/contrib/jackc/pgx.v5/metrics.go b/contrib/jackc/pgx.v5/metrics.go index e87a710c8f..974e85f0fb 100644 --- a/contrib/jackc/pgx.v5/metrics.go +++ b/contrib/jackc/pgx.v5/metrics.go @@ -34,7 +34,7 @@ const ( var interval = 10 * time.Second // pollPoolStats calls (*pgxpool).Stats on the pool at a predetermined interval. It pushes the pool Stats off to the statsd client. -func pollPoolStats(statsd internal.StatsdClient, pool *pgxpool.Pool, stop chan struct{}) { +func pollPoolStats(statsd internal.StatsdClient, pool *pgxpool.Pool) { // TODO: Create stop condition for pgx on db.Close log.Debug("contrib/jackc/pgx.v5: Traced pool connection found: Pool stats will be gathered and sent every %v.", interval) ticker := time.NewTicker(interval) @@ -56,8 +56,6 @@ func pollPoolStats(statsd internal.StatsdClient, pool *pgxpool.Pool, stop chan s statsd.Gauge(NewConnsCount, float64(stat.NewConnsCount()), []string{}, 1) statsd.Gauge(MaxLifetimeDestroyCount, float64(stat.MaxLifetimeDestroyCount()), []string{}, 1) statsd.Gauge(MaxIdleDestroyCount, float64(stat.MaxIdleDestroyCount()), []string{}, 1) - case <-stop: - return } } } diff --git a/contrib/jackc/pgx.v5/pgxpool.go b/contrib/jackc/pgx.v5/pgxpool.go index 7f2b677305..11d93b0859 100644 --- a/contrib/jackc/pgx.v5/pgxpool.go +++ b/contrib/jackc/pgx.v5/pgxpool.go @@ -9,7 +9,6 @@ import ( "context" "github.com/jackc/pgx/v5/pgxpool" - "gopkg.in/DataDog/dd-trace-go.v1/internal/contribroutines" ) func NewPool(ctx context.Context, connString string, opts ...Option) (*pgxpool.Pool, error) { @@ -31,7 +30,7 @@ func NewPoolWithConfig(ctx context.Context, config *pgxpool.Config, opts ...Opti return nil, err } if tracer.cfg.poolStats && tracer.cfg.statsdClient != nil { - go pollPoolStats(tracer.cfg.statsdClient, pool, contribroutines.GetStopChan()) + go pollPoolStats(tracer.cfg.statsdClient, pool) } return pool, nil } diff --git a/ddtrace/tracer/tracer.go b/ddtrace/tracer/tracer.go index 5cf6cb231e..25e08b7cb3 100644 --- a/ddtrace/tracer/tracer.go +++ b/ddtrace/tracer/tracer.go @@ -24,7 +24,6 @@ import ( globalinternal "gopkg.in/DataDog/dd-trace-go.v1/internal" "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec" appsecConfig "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/config" - "gopkg.in/DataDog/dd-trace-go.v1/internal/contribroutines" "gopkg.in/DataDog/dd-trace-go.v1/internal/datastreams" "gopkg.in/DataDog/dd-trace-go.v1/internal/hostname" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" @@ -204,7 +203,6 @@ func Start(opts ...StartOption) { } _ = t.hostname() // Prime the hostname cache - contribroutines.InitStopChan() } // Stop stops the started tracer. Subsequent calls are valid but become no-op. @@ -729,7 +727,6 @@ func (t *tracer) Stop() { if t.logFile != nil { t.logFile.Close() } - contribroutines.Stop() } // Inject uses the configured or default TextMap Propagator. diff --git a/ddtrace/tracer/tracer_test.go b/ddtrace/tracer/tracer_test.go index 5d62572a6c..dd1820621e 100644 --- a/ddtrace/tracer/tracer_test.go +++ b/ddtrace/tracer/tracer_test.go @@ -30,7 +30,6 @@ import ( "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal" maininternal "gopkg.in/DataDog/dd-trace-go.v1/internal" - "gopkg.in/DataDog/dd-trace-go.v1/internal/contribroutines" "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" "gopkg.in/DataDog/dd-trace-go.v1/internal/statsdtest" @@ -256,27 +255,6 @@ func TestTracerStart(t *testing.T) { tr.Stop() tr.Stop() }) - t.Run("contribroutines", func(t *testing.T) { - // assert tracer.Start initializes contribroutines.stop - Start() - s1 := contribroutines.GetStopChan() - assert.NotNil(t, s1) - - // assert tracer.Stop closes the channel - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - <-s1 - }() - Stop() - wg.Wait() - - // assert tracer.Start initializes contribroutines.stop to a new channel every time - Start() - s2 := contribroutines.GetStopChan() - assert.NotEqual(t, s1, s2) - }) } func TestTracerLogFile(t *testing.T) { diff --git a/internal/contribroutines/contribroutines.go b/internal/contribroutines/contribroutines.go deleted file mode 100644 index eb977a463c..0000000000 --- a/internal/contribroutines/contribroutines.go +++ /dev/null @@ -1,34 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed -// under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016 Datadog, Inc. -package contribroutines - -import "sync" - -var ( - stop chan struct{} - once sync.Once - mu sync.Mutex -) - -func InitStopChan() { - stop = make(chan struct{}) -} - -func Stop() { - mu.Lock() - defer mu.Unlock() - if stop == nil { - InitStopChan() - } - once.Do(func() { - close(stop) - }) -} - -func GetStopChan() chan struct{} { - mu.Lock() - defer mu.Unlock() - return stop -} diff --git a/internal/contribroutines/contribroutines_test.go b/internal/contribroutines/contribroutines_test.go deleted file mode 100644 index c54aae09e2..0000000000 --- a/internal/contribroutines/contribroutines_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed -// under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016 Datadog, Inc. -package contribroutines - -import ( - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestContribRoutines(t *testing.T) { - var wg sync.WaitGroup - wg.Add(1) - var done bool - go func() { - doSomething(&wg, &done, GetStopChan()) - }() - Stop() - wg.Wait() - assert.True(t, done) -} - -func doSomething(wg *sync.WaitGroup, done *bool, stop chan struct{}) { - defer wg.Done() - ticker := time.NewTicker(500 * time.Millisecond) - defer ticker.Stop() - for { - select { - case <-ticker.C: - case <-stop: - *done = true - return - } - } -} - -func TestStopConcurrency(t *testing.T) { - var wg sync.WaitGroup - - for i := 0; i < 100; i++ { - wg.Add(1) - go func() { - defer wg.Done() - Stop() - }() - } - - wg.Wait() - - select { - case <-stop: - // channel is closed, so Stop() was called successfully - case <-time.After(1 * time.Second): - t.Error("stop channel was not closed within 1 second") - } -} - -func TestGetStopChanConcurrency(t *testing.T) { - var wg sync.WaitGroup - - for i := 0; i < 100; i++ { - wg.Add(1) - go func() { - defer wg.Done() - GetStopChan() - }() - } - - wg.Wait() -} From 5bb8a4cfb57ac9a9df7e676381967bfbeea08fd1 Mon Sep 17 00:00:00 2001 From: Mikayla Toffler Date: Fri, 17 Jan 2025 12:22:15 -0500 Subject: [PATCH 17/17] remove unused helper fn --- contrib/database/sql/sql_test.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/contrib/database/sql/sql_test.go b/contrib/database/sql/sql_test.go index 651525fadd..e4d587b8ea 100644 --- a/contrib/database/sql/sql_test.go +++ b/contrib/database/sql/sql_test.go @@ -7,7 +7,6 @@ package sql import ( "context" - "database/sql" "database/sql/driver" "errors" "fmt" @@ -536,12 +535,3 @@ func TestNamingSchema(t *testing.T) { t.Run("SpanName", namingschematest.NewSpanNameTest(genSpans, assertOpV0, assertOpV1)) }) } - -func setupPostgres(t *testing.T) *sql.DB { - driverName := "postgres" - Register(driverName, &pq.Driver{}) - defer unregister(driverName) - db, err := Open(driverName, "postgres://postgres:postgres@127.0.0.1:5432/postgres?sslmode=disable") - require.NoError(t, err) - return db -}