Skip to content

Commit b015534

Browse files
committed
pkg/sqlutil/pg: create package; expand env config; add example relay
1 parent a946a57 commit b015534

File tree

14 files changed

+879
-42
lines changed

14 files changed

+879
-42
lines changed

go.mod

+10
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/smartcontractkit/chainlink-common
33
go 1.23.3
44

55
require (
6+
github.com/XSAM/otelsql v0.29.0
67
github.com/andybalholm/brotli v1.1.0
78
github.com/atombender/go-jsonschema v0.16.1-0.20240916205339-a74cd4e2851c
89
github.com/bytecodealliance/wasmtime-go/v23 v23.0.0
@@ -20,6 +21,7 @@ require (
2021
github.com/hashicorp/go-plugin v1.6.2
2122
github.com/iancoleman/strcase v0.3.0
2223
github.com/invopop/jsonschema v0.12.0
24+
github.com/jackc/pgx/v4 v4.18.3
2325
github.com/jmoiron/sqlx v1.4.0
2426
github.com/jonboulle/clockwork v0.4.0
2527
github.com/jpillora/backoff v1.0.0
@@ -29,6 +31,7 @@ require (
2931
github.com/prometheus/client_golang v1.17.0
3032
github.com/riferrei/srclient v0.5.4
3133
github.com/santhosh-tekuri/jsonschema/v5 v5.2.0
34+
github.com/scylladb/go-reflectx v1.0.1
3235
github.com/shopspring/decimal v1.4.0
3336
github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7
3437
github.com/smartcontractkit/libocr v0.0.0-20241007185508-adbe57025f12
@@ -79,6 +82,13 @@ require (
7982
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect
8083
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
8184
github.com/hashicorp/yamux v0.1.1 // indirect
85+
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
86+
github.com/jackc/pgconn v1.14.3 // indirect
87+
github.com/jackc/pgio v1.0.0 // indirect
88+
github.com/jackc/pgpassfile v1.0.0 // indirect
89+
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
90+
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
91+
github.com/jackc/pgtype v1.14.0 // indirect
8292
github.com/leodido/go-urn v1.2.0 // indirect
8393
github.com/mailru/easyjson v0.7.7 // indirect
8494
github.com/mattn/go-colorable v0.1.13 // indirect

go.sum

+134
Large diffs are not rendered by default.

pkg/config/build/build.go

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package build
2+
3+
import (
4+
"os"
5+
"runtime/debug"
6+
)
7+
8+
// Unset is a sentinel value.
9+
const Unset = "unset"
10+
11+
// Version and Checksum are set at compile time via build arguments.
12+
var (
13+
// Program is updated to the full main program path if [debug.BuildInfo] is available.
14+
Program = os.Args[0]
15+
// Version is the semantic version of the build or Unset.
16+
Version = Unset
17+
// Checksum is the commit hash of the build or Unset.
18+
Checksum = Unset
19+
ChecksumPrefix = Unset
20+
)
21+
22+
func init() {
23+
buildInfo, ok := debug.ReadBuildInfo()
24+
if ok {
25+
Program = buildInfo.Main.Path
26+
if Version == Unset && buildInfo.Main.Version != "" {
27+
Version = buildInfo.Main.Version
28+
}
29+
if Checksum == Unset && buildInfo.Main.Sum != "" {
30+
Checksum = buildInfo.Main.Sum
31+
}
32+
}
33+
ChecksumPrefix = Checksum[:min(7, len(Checksum))]
34+
}

pkg/logger/logger.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ package logger
22

33
import (
44
"io"
5+
"fmt"
56
"reflect"
67
"testing"
78

89
"go.uber.org/zap"
910
"go.uber.org/zap/zapcore"
1011
"go.uber.org/zap/zaptest"
1112
"go.uber.org/zap/zaptest/observer"
13+
14+
"github.com/smartcontractkit/chainlink-common/pkg/config/build"
1215
)
1316

1417
// Logger is a minimal subset of smartcontractkit/chainlink/core/logger.Logger implemented by go.uber.org/zap.SugaredLogger
@@ -52,9 +55,16 @@ func New() (Logger, error) { return defaultConfig.New() }
5255
func (c *Config) New() (Logger, error) {
5356
return NewWith(func(cfg *zap.Config) {
5457
cfg.Level.SetLevel(c.Level)
58+
cfg.InitialFields = map[string]interface{}{
59+
"version": buildVersion(),
60+
}
5561
})
5662
}
5763

64+
func buildVersion() string {
65+
return fmt.Sprintf("%s@%s", build.Version, build.ChecksumPrefix)
66+
}
67+
5868
// NewWith returns a new Logger from a modified [zap.Config].
5969
func NewWith(cfgFn func(*zap.Config)) (Logger, error) {
6070
cfg := zap.NewProductionConfig()
@@ -83,7 +93,7 @@ func Test(tb testing.TB) Logger {
8393
zapcore.DebugLevel,
8494
),
8595
)
86-
return &logger{lggr.Sugar()}
96+
return &logger{lggr.With(zap.String("version", buildVersion())).Sugar()}
8797
}
8898

8999
// TestSugared returns a new test SugaredLogger.

pkg/loop/config.go

+62-16
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,15 @@ import (
1212
)
1313

1414
const (
15-
envDatabaseURL = "CL_DATABASE_URL"
16-
envPromPort = "CL_PROMETHEUS_PORT"
15+
envDatabaseURL = "CL_DATABASE_URL"
16+
envDatabaseIdleInTxSessionTimeout = "CL_DATABASE_IDLE_IN_TX_SESSION_TIMEOUT"
17+
envDatabaseLockTimeout = "CL_DATABASE_LOCK_TIMEOUT"
18+
envDatabaseQueryTimeout = "CL_DATABASE_QUERY_TIMEOUT"
19+
envDatabaseLogSQL = "CL_DATABASE_LOG_SQL"
20+
envDatabaseMaxOpenConns = "CL_DATABASE_MAX_OPEN_CONNS"
21+
envDatabaseMaxIdleConns = "CL_DATABASE_MAX_IDLE_CONNS"
22+
23+
envPromPort = "CL_PROMETHEUS_PORT"
1724

1825
envTracingEnabled = "CL_TRACING_ENABLED"
1926
envTracingCollectorTarget = "CL_TRACING_COLLECTOR_TARGET"
@@ -36,7 +43,13 @@ const (
3643
// EnvConfig is the configuration between the application and the LOOP executable. The values
3744
// are fully resolved and static and passed via the environment.
3845
type EnvConfig struct {
39-
DatabaseURL *url.URL
46+
DatabaseURL *url.URL
47+
DatabaseIdleInTxSessionTimeout time.Duration
48+
DatabaseLockTimeout time.Duration
49+
DatabaseQueryTimeout time.Duration
50+
DatabaseLogSQL bool
51+
DatabaseMaxOpenConns int
52+
DatabaseMaxIdleConns int
4053

4154
PrometheusPort int
4255

@@ -66,7 +79,14 @@ func (e *EnvConfig) AsCmdEnv() (env []string) {
6679

6780
if e.DatabaseURL != nil { // optional
6881
add(envDatabaseURL, e.DatabaseURL.String())
82+
add(envDatabaseIdleInTxSessionTimeout, e.DatabaseIdleInTxSessionTimeout.String())
83+
add(envDatabaseLockTimeout, e.DatabaseLockTimeout.String())
84+
add(envDatabaseQueryTimeout, e.DatabaseQueryTimeout.String())
85+
add(envDatabaseLogSQL, strconv.FormatBool(e.DatabaseLogSQL))
86+
add(envDatabaseMaxOpenConns, strconv.Itoa(e.DatabaseMaxOpenConns))
87+
add(envDatabaseMaxIdleConns, strconv.Itoa(e.DatabaseMaxIdleConns))
6988
}
89+
7090
add(envPromPort, strconv.Itoa(e.PrometheusPort))
7191

7292
add(envTracingEnabled, strconv.FormatBool(e.TracingEnabled))
@@ -99,13 +119,44 @@ func (e *EnvConfig) AsCmdEnv() (env []string) {
99119

100120
// parse deserializes environment variables
101121
func (e *EnvConfig) parse() error {
102-
promPortStr := os.Getenv(envPromPort)
103122
var err error
104-
e.DatabaseURL, err = getDatabaseURL()
123+
e.DatabaseURL, err = getEnv(envDatabaseURL, func(s string) (*url.URL, error) {
124+
if s == "" { // DatabaseURL is optional
125+
return nil, nil
126+
}
127+
return url.Parse(s)
128+
})
105129
if err != nil {
106-
return fmt.Errorf("failed to parse %s: %w", envDatabaseURL, err)
130+
return err
131+
}
132+
if e.DatabaseURL != nil {
133+
e.DatabaseIdleInTxSessionTimeout, err = getEnv(envDatabaseIdleInTxSessionTimeout, time.ParseDuration)
134+
if err != nil {
135+
return err
136+
}
137+
e.DatabaseLockTimeout, err = getEnv(envDatabaseLockTimeout, time.ParseDuration)
138+
if err != nil {
139+
return err
140+
}
141+
e.DatabaseQueryTimeout, err = getEnv(envDatabaseQueryTimeout, time.ParseDuration)
142+
if err != nil {
143+
return err
144+
}
145+
e.DatabaseLogSQL, err = getEnv(envDatabaseLogSQL, strconv.ParseBool)
146+
if err != nil {
147+
return err
148+
}
149+
e.DatabaseMaxOpenConns, err = getEnv(envDatabaseMaxOpenConns, strconv.Atoi)
150+
if err != nil {
151+
return err
152+
}
153+
e.DatabaseMaxIdleConns, err = getEnv(envDatabaseMaxIdleConns, strconv.Atoi)
154+
if err != nil {
155+
return err
156+
}
107157
}
108158

159+
promPortStr := os.Getenv(envPromPort)
109160
e.PrometheusPort, err = strconv.Atoi(promPortStr)
110161
if err != nil {
111162
return fmt.Errorf("failed to parse %s = %q: %w", envPromPort, promPortStr, err)
@@ -203,16 +254,11 @@ func getFloat64OrZero(envKey string) float64 {
203254
return f
204255
}
205256

206-
// getDatabaseURL parses the CL_DATABASE_URL environment variable.
207-
func getDatabaseURL() (*url.URL, error) {
208-
databaseURL := os.Getenv(envDatabaseURL)
209-
if databaseURL == "" {
210-
// DatabaseURL is optional
211-
return nil, nil
212-
}
213-
u, err := url.Parse(databaseURL)
257+
func getEnv[T any](key string, parse func(string) (T, error)) (t T, err error) {
258+
v := os.Getenv(key)
259+
t, err = parse(v)
214260
if err != nil {
215-
return nil, fmt.Errorf("invalid %s: %w", envDatabaseURL, err)
261+
err = fmt.Errorf("failed to parse %s=%s: %w", key, v, err)
216262
}
217-
return u, nil
263+
return
218264
}

pkg/loop/config_test.go

+49-7
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,18 @@ import (
1818

1919
func TestEnvConfig_parse(t *testing.T) {
2020
cases := []struct {
21-
name string
22-
envVars map[string]string
23-
expectError bool
24-
expectedDatabaseURL string
21+
name string
22+
envVars map[string]string
23+
expectError bool
24+
25+
expectedDatabaseURL string
26+
expectedDatabaseIdleInTxSessionTimeout time.Duration
27+
expectedDatabaseLockTimeout time.Duration
28+
expectedDatabaseQueryTimeout time.Duration
29+
expectedDatabaseLogSQL bool
30+
expectedDatabaseMaxOpenConns int
31+
expectedDatabaseMaxIdleConns int
32+
2533
expectedPrometheusPort int
2634
expectedTracingEnabled bool
2735
expectedTracingCollectorTarget string
@@ -31,16 +39,31 @@ func TestEnvConfig_parse(t *testing.T) {
3139
{
3240
name: "All variables set correctly",
3341
envVars: map[string]string{
34-
envDatabaseURL: "postgres://user:password@localhost:5432/db",
42+
envDatabaseURL: "postgres://user:password@localhost:5432/db",
43+
envDatabaseIdleInTxSessionTimeout: "42s",
44+
envDatabaseLockTimeout: "8m",
45+
envDatabaseQueryTimeout: "7s",
46+
envDatabaseLogSQL: "true",
47+
envDatabaseMaxOpenConns: "9999",
48+
envDatabaseMaxIdleConns: "8080",
49+
3550
envPromPort: "8080",
3651
envTracingEnabled: "true",
3752
envTracingCollectorTarget: "some:target",
3853
envTracingSamplingRatio: "1.0",
3954
envTracingTLSCertPath: "internal/test/fixtures/client.pem",
4055
envTracingAttribute + "XYZ": "value",
4156
},
42-
expectError: false,
43-
expectedDatabaseURL: "postgres://user:password@localhost:5432/db",
57+
expectError: false,
58+
59+
expectedDatabaseURL: "postgres://user:password@localhost:5432/db",
60+
expectedDatabaseIdleInTxSessionTimeout: 42 * time.Second,
61+
expectedDatabaseLockTimeout: 8 * time.Minute,
62+
expectedDatabaseQueryTimeout: 7 * time.Second,
63+
expectedDatabaseLogSQL: true,
64+
expectedDatabaseMaxOpenConns: 9999,
65+
expectedDatabaseMaxIdleConns: 8080,
66+
4467
expectedPrometheusPort: 8080,
4568
expectedTracingEnabled: true,
4669
expectedTracingCollectorTarget: "some:target",
@@ -91,6 +114,25 @@ func TestEnvConfig_parse(t *testing.T) {
91114
if config.DatabaseURL.String() != tc.expectedDatabaseURL {
92115
t.Errorf("Expected Database URL %s, got %s", tc.expectedDatabaseURL, config.DatabaseURL)
93116
}
117+
if config.DatabaseIdleInTxSessionTimeout != tc.expectedDatabaseIdleInTxSessionTimeout {
118+
t.Errorf("Expected Database idle in tx session timeout %s, got %s", tc.expectedDatabaseIdleInTxSessionTimeout, config.DatabaseIdleInTxSessionTimeout)
119+
}
120+
if config.DatabaseLockTimeout != tc.expectedDatabaseLockTimeout {
121+
t.Errorf("Expected Database lock timeout %s, got %s", tc.expectedDatabaseLockTimeout, config.DatabaseLockTimeout)
122+
}
123+
if config.DatabaseQueryTimeout != tc.expectedDatabaseQueryTimeout {
124+
t.Errorf("Expected Database query timeout %s, got %s", tc.expectedDatabaseQueryTimeout, config.DatabaseQueryTimeout)
125+
}
126+
if config.DatabaseLogSQL != tc.expectedDatabaseLogSQL {
127+
t.Errorf("Expected Database log sql %t, got %t", tc.expectedDatabaseLogSQL, config.DatabaseLogSQL)
128+
}
129+
if config.DatabaseMaxOpenConns != tc.expectedDatabaseMaxOpenConns {
130+
t.Errorf("Expected Database max open conns %d, got %d", tc.expectedDatabaseMaxOpenConns, config.DatabaseMaxOpenConns)
131+
}
132+
if config.DatabaseMaxIdleConns != tc.expectedDatabaseMaxIdleConns {
133+
t.Errorf("Expected Database max idle conns %d, got %d", tc.expectedDatabaseMaxIdleConns, config.DatabaseMaxIdleConns)
134+
}
135+
94136
if config.PrometheusPort != tc.expectedPrometheusPort {
95137
t.Errorf("Expected Prometheus port %d, got %d", tc.expectedPrometheusPort, config.PrometheusPort)
96138
}

0 commit comments

Comments
 (0)