Skip to content

Commit 5b29bfe

Browse files
authored
Add Go Postgres utils (#272)
1 parent 493baf6 commit 5b29bfe

File tree

5 files changed

+221
-1
lines changed

5 files changed

+221
-1
lines changed

MODULE.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ use_repo(
212212
"com_github_gokrazy_rsync",
213213
"com_github_google_shlex",
214214
"com_github_gorilla_websocket",
215+
"com_github_jackc_pgx_v5",
215216
"in_gopkg_yaml_v3",
216217

217218
# Dependencies for building rsync for CLI distribution

src/go.mod

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,23 @@ require (
88
github.com/gokrazy/rsync v0.0.0-20250601185929-d3cb1d4a4fcd
99
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
1010
github.com/gorilla/websocket v1.5.0
11+
github.com/jackc/pgx/v5 v5.7.2
1112
gopkg.in/yaml.v3 v3.0.1
1213
)
1314

1415
require (
1516
github.com/google/renameio/v2 v2.0.0 // indirect
17+
github.com/jackc/pgpassfile v1.0.0 // indirect
18+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
19+
github.com/jackc/puddle/v2 v2.2.2 // indirect
20+
github.com/kr/text v0.2.0 // indirect
1621
github.com/landlock-lsm/go-landlock v0.0.0-20250303204525-1544bccde3a3 // indirect
1722
github.com/mmcloughlin/md4 v0.1.2 // indirect
23+
github.com/rogpeppe/go-internal v1.14.1 // indirect
24+
golang.org/x/crypto v0.37.0 // indirect
1825
golang.org/x/sync v0.13.0 // indirect
1926
golang.org/x/sys v0.32.0 // indirect
27+
golang.org/x/text v0.24.0 // indirect
2028
golang.org/x/time v0.3.0 // indirect
2129
kernel.org/pub/linux/libs/security/libcap/psx v1.2.76 // indirect
2230
)

src/go.sum

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
github.com/conduitio/bwlimit v0.1.0 h1:x3ijON0TSghQob4tFKaEvKixFmYKfVJQeSpXluC2JvE=
22
github.com/conduitio/bwlimit v0.1.0/go.mod h1:E+ASZ1/5L33MTb8hJTERs5Xnmh6Ulq3jbRh7LrdbXWU=
3+
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
34
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
45
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
6+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
8+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
59
github.com/gokrazy/rsync v0.0.0-20250601185929-d3cb1d4a4fcd h1:SF3hnrM/YPI+GQJnWq2ldcWZ0Y6Bdm+VP3KItdoxRL4=
610
github.com/gokrazy/rsync v0.0.0-20250601185929-d3cb1d4a4fcd/go.mod h1:nrvfy+3qYcxt92pGtVa38uKlQ0dl2SrXEmtIaY/vCHA=
711
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
@@ -12,18 +16,45 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaU
1216
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
1317
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
1418
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
19+
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
20+
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
21+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
22+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
23+
github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI=
24+
github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
25+
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
26+
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
27+
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
28+
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
29+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
30+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
1531
github.com/landlock-lsm/go-landlock v0.0.0-20250303204525-1544bccde3a3 h1:zcMi8R8vP0WrrXlFMNUBpDy/ydo3sTnCcUPowq1XmSc=
1632
github.com/landlock-lsm/go-landlock v0.0.0-20250303204525-1544bccde3a3/go.mod h1:RSub3ourNF8Hf+swvw49Catm3s7HVf4hzdFxDUnEzdA=
1733
github.com/mmcloughlin/md4 v0.1.2 h1:kGYl+iNbxhyz4u76ka9a+0TXP9KWt/LmnM0QhZwhcBo=
1834
github.com/mmcloughlin/md4 v0.1.2/go.mod h1:AAxFX59fddW0IguqNzWlf1lazh1+rXeIt/Bj49cqDTQ=
35+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
36+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
37+
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
38+
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
39+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
40+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
41+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
42+
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
43+
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
44+
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
45+
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
1946
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
2047
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
2148
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
2249
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
50+
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
51+
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
2352
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
2453
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
25-
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
2654
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
55+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
56+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
57+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
2758
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
2859
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
2960
kernel.org/pub/linux/libs/security/libcap/psx v1.2.76 h1:3DyzQ30OHt3wiOZVL1se2g1PAPJIU7+tMUyvfMUj1dY=

src/utils/postgres/BUILD

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
# SPDX-License-Identifier: Apache-2.0
16+
17+
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
18+
19+
go_library(
20+
name = "postgres",
21+
srcs = [
22+
"postgres_client.go",
23+
],
24+
importpath = "go.corp.nvidia.com/osmo/utils/postgres",
25+
visibility = ["//visibility:public"],
26+
deps = [
27+
"@com_github_jackc_pgx_v5//pgxpool:go_default_library",
28+
],
29+
)
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
16+
SPDX-License-Identifier: Apache-2.0
17+
*/
18+
19+
package postgres
20+
21+
import (
22+
"context"
23+
"fmt"
24+
"log/slog"
25+
"time"
26+
27+
"github.com/jackc/pgx/v5/pgxpool"
28+
)
29+
30+
// PostgresConfig holds PostgreSQL connection configuration
31+
type PostgresConfig struct {
32+
Host string
33+
Port int
34+
Database string
35+
User string
36+
Password string
37+
MaxConns int32
38+
MinConns int32
39+
MaxConnLifetime time.Duration
40+
SSLMode string
41+
}
42+
43+
// PostgresClient handles PostgreSQL database operations
44+
type PostgresClient struct {
45+
pool *pgxpool.Pool
46+
logger *slog.Logger
47+
}
48+
49+
// NewPostgresClientFromParams creates a new PostgreSQL client with the given connection parameters.
50+
// This is a convenience function that constructs a PostgresConfig and calls NewPostgresClient.
51+
func CreatePostgresClient(
52+
ctx context.Context,
53+
logger *slog.Logger,
54+
host string,
55+
port int,
56+
database string,
57+
user string,
58+
password string,
59+
maxConns int32,
60+
minConns int32,
61+
maxConnLifetime time.Duration,
62+
sslMode string,
63+
) (*PostgresClient, error) {
64+
config := PostgresConfig{
65+
Host: host,
66+
Port: port,
67+
Database: database,
68+
User: user,
69+
Password: password,
70+
MaxConns: maxConns,
71+
MinConns: minConns,
72+
MaxConnLifetime: maxConnLifetime,
73+
SSLMode: sslMode,
74+
}
75+
76+
client, err := NewPostgresClient(ctx, config, logger)
77+
if err != nil {
78+
return nil, err
79+
}
80+
81+
logger.Info("postgres client initialized",
82+
slog.String("host", host),
83+
slog.Int("port", port),
84+
slog.String("database", database),
85+
)
86+
87+
return client, nil
88+
}
89+
90+
// NewPostgresClient creates a new PostgreSQL client with connection pooling
91+
func NewPostgresClient(ctx context.Context, config PostgresConfig, logger *slog.Logger) (*PostgresClient, error) {
92+
// Build connection URL
93+
connURL := fmt.Sprintf(
94+
"postgres://%s:%s@%s:%d/%s?sslmode=%s",
95+
config.User,
96+
config.Password,
97+
config.Host,
98+
config.Port,
99+
config.Database,
100+
config.SSLMode,
101+
)
102+
103+
// Parse config to get a pgxpool.Config
104+
poolConfig, err := pgxpool.ParseConfig(connURL)
105+
if err != nil {
106+
return nil, fmt.Errorf("failed to parse connection config: %w", err)
107+
}
108+
109+
// Configure connection pool settings
110+
poolConfig.MaxConns = config.MaxConns
111+
poolConfig.MinConns = config.MinConns
112+
poolConfig.MaxConnLifetime = config.MaxConnLifetime
113+
114+
// Create connection pool
115+
pool, err := pgxpool.NewWithConfig(ctx, poolConfig)
116+
if err != nil {
117+
return nil, fmt.Errorf("failed to create connection pool: %w", err)
118+
}
119+
120+
// Ping to verify connection
121+
pingCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
122+
defer cancel()
123+
124+
if err := pool.Ping(pingCtx); err != nil {
125+
pool.Close()
126+
return nil, fmt.Errorf("failed to ping database: %w", err)
127+
}
128+
129+
logger.Info("postgres client connected successfully")
130+
131+
return &PostgresClient{
132+
pool: pool,
133+
logger: logger,
134+
}, nil
135+
}
136+
137+
// Close closes the database connection pool
138+
func (c *PostgresClient) Close() {
139+
c.logger.Info("closing postgres client")
140+
c.pool.Close()
141+
}
142+
143+
// Pool returns the underlying pgxpool.Pool for direct database access
144+
func (c *PostgresClient) Pool() *pgxpool.Pool {
145+
return c.pool
146+
}
147+
148+
// Ping verifies the database connection is still alive
149+
func (c *PostgresClient) Ping(ctx context.Context) error {
150+
return c.pool.Ping(ctx)
151+
}

0 commit comments

Comments
 (0)