-
-
Notifications
You must be signed in to change notification settings - Fork 186
feat: shared pgx-pool #4393
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: shared pgx-pool #4393
Changes from 8 commits
6823c96
7a3eb53
a0082b1
e9ff21c
117ae1a
4617a05
5d97bb0
cde1664
2852577
fa090e2
228208a
de4ed7f
e3f6a11
a0bd5ce
39388bb
b990bbe
d290db0
73d9ed6
9f25828
6ffb852
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| package postgresqlretriever | ||
|
|
||
| import ( | ||
| "context" | ||
| "sync" | ||
|
|
||
| "github.com/jackc/pgx/v5/pgxpool" | ||
| ) | ||
|
|
||
| type poolEntry struct { | ||
| pool *pgxpool.Pool | ||
| refCount int | ||
| } | ||
|
|
||
| var ( | ||
| mu sync.Mutex | ||
| poolMap = make(map[string]*poolEntry) | ||
| ) | ||
|
|
||
| // GetPool returns a pool for a given URI, creating it if needed. | ||
| func GetPool(ctx context.Context, uri string) (*pgxpool.Pool, error) { | ||
|
Check warning on line 21 in retriever/postgresqlretriever/postgres.go
|
||
| mu.Lock() | ||
| defer mu.Unlock() | ||
|
|
||
| // If already exists, bump refCount | ||
| if entry, ok := poolMap[uri]; ok { | ||
| entry.refCount++ | ||
| return entry.pool, nil | ||
| } | ||
|
|
||
| // Create a new pool | ||
| pool, err := pgxpool.New(ctx, uri) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| if err := pool.Ping(ctx); err != nil { | ||
| pool.Close() | ||
| return nil, err | ||
| } | ||
|
|
||
| poolMap[uri] = &poolEntry{ | ||
| pool: pool, | ||
| refCount: 1, | ||
| } | ||
|
|
||
| return pool, nil | ||
| } | ||
|
Comment on lines
+21
to
+47
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current implementation of To improve this, you can use a double-checked locking pattern. This avoids holding the lock during the potentially long-running pool creation and pinging process. func GetPool(ctx context.Context, uri string) (*pgxpool.Pool, error) {
mu.Lock()
if entry, ok := poolMap[uri]; ok {
entry.refCount++
mu.Unlock()
return entry.pool, nil
}
mu.Unlock()
// Create a new pool outside the lock.
pool, err := pgxpool.New(ctx, uri)
if err != nil {
return nil, err
}
if err := pool.Ping(ctx); err != nil {
pool.Close()
return nil, err
}
mu.Lock()
defer mu.Unlock()
// Re-check if another goroutine created the pool while we were unlocked.
if entry, ok := poolMap[uri]; ok {
pool.Close() // Close the one we just created, it's not needed.
entry.refCount++
return entry.pool, nil
}
poolMap[uri] = &poolEntry{
pool: pool,
refCount: 1,
}
return pool, nil
} |
||
|
|
||
| // ReleasePool decreases refCount and closes/removes when it hits zero. | ||
| func ReleasePool(uri string) { | ||
gogbog marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| mu.Lock() | ||
| defer mu.Unlock() | ||
|
|
||
| entry, ok := poolMap[uri] | ||
| if !ok { | ||
| return // nothing to do | ||
| } | ||
|
|
||
| entry.refCount-- | ||
| if entry.refCount <= 0 { | ||
| entry.pool.Close() | ||
| delete(poolMap, uri) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The use of global variables (
muandpoolMap) to implement the singleton pattern for the connection pool is a common approach for shared resources. However, global state can sometimes make testing more complex and introduce implicit dependencies. While acceptable for this specific use case, it's a design choice that should be considered carefully in larger architectures.