Skip to content

Commit c546ea4

Browse files
Add in provider options for DB Connection Pools (#3)
* Add in provider options for DB Connection Pools The defaults maintain the current configuration, but this allows users to tune db connections for individual needs. In particular, even with the latest go libraries, we often encounter "cannot allocate memory" errors when creating yet another DB connection. We have a large number of resources across multiple DBs, so this happens frequently. For our uses, enabling connection pooling is going to save us a lot of headaches and the edge case disabling the connection pooling is guarding against unlikely to be an issue. * Use `any` and add some documentation for connection timing
1 parent fa39f07 commit c546ea4

File tree

2 files changed

+59
-4
lines changed

2 files changed

+59
-4
lines changed

postgresql/config.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"strconv"
99
"strings"
1010
"sync"
11+
"time"
1112
"unicode"
1213

1314
"github.com/blang/semver"
@@ -179,6 +180,9 @@ type Config struct {
179180
Timeout int
180181
ConnectTimeoutSec int
181182
MaxConns int
183+
MaxIdleConns int
184+
ConnMaxIdleTime time.Duration
185+
ConnMaxLifetime time.Duration
182186
ExpectedVersion semver.Version
183187
SSLClientCert *ClientCertificateConfig
184188
SSLRootCertPath string
@@ -308,8 +312,10 @@ func (c *Client) Connect() (*DBConnection, error) {
308312
// We don't want to retain connection
309313
// So when we connect on a specific database which might be managed by terraform,
310314
// we don't keep opened connection in case of the db has to be dropped in the plan.
311-
db.SetMaxIdleConns(0)
315+
db.SetMaxIdleConns(c.config.MaxIdleConns)
312316
db.SetMaxOpenConns(c.config.MaxConns)
317+
db.SetConnMaxIdleTime(c.config.ConnMaxIdleTime)
318+
db.SetConnMaxIdleTime(c.config.ConnMaxLifetime)
313319

314320
defaultVersion, _ := semver.Parse(defaultExpectedPostgreSQLVersion)
315321
version := &c.config.ExpectedVersion

postgresql/provider.go

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package postgresql
33
import (
44
"context"
55
"fmt"
6+
"os"
7+
"time"
8+
69
"github.com/aws/aws-sdk-go-v2/credentials"
710
"github.com/aws/aws-sdk-go-v2/service/sts"
8-
"os"
911

1012
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
1113
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
@@ -21,8 +23,12 @@ import (
2123
)
2224

2325
const (
24-
defaultProviderMaxOpenConnections = 20
25-
defaultExpectedPostgreSQLVersion = "9.0.0"
26+
defaultProviderMaxOpenConnections = 20 // sql.DB Default
27+
defaultProviderMaxIdleConnections = 0
28+
// Keep connection lifetimes short by default, to prevent blocking 'destroy' actions on database instance resources.
29+
defaultProviderConnectionMaxIdleTime = time.Second * 5
30+
defaultProviderConnectionMaxLifetime = time.Second * 10
31+
defaultExpectedPostgreSQLVersion = "9.0.0"
2632
)
2733

2834
// Provider returns a terraform.ResourceProvider.
@@ -192,6 +198,35 @@ func Provider() *schema.Provider {
192198
Description: "Maximum number of connections to establish to the database. Zero means unlimited.",
193199
ValidateFunc: validation.IntAtLeast(-1),
194200
},
201+
"max_idle_connections": {
202+
Type: schema.TypeInt,
203+
Optional: true,
204+
Default: defaultProviderMaxIdleConnections,
205+
Description: "Maximum number of idle connections to hold for the database. " +
206+
"NOTE: Idle connections that aren't cleaned-up can cause problems if a database is destroyed in " +
207+
"the same plan. The default is 0 to prevent that issue.",
208+
ValidateFunc: validation.IntAtLeast(-1),
209+
},
210+
"connection_max_idle_time": {
211+
Type: schema.TypeString,
212+
Optional: true,
213+
Default: defaultProviderConnectionMaxIdleTime.String(),
214+
Description: "Duration to hold idle connections to the database. Setting to 0 will hold connections " + "" +
215+
"forever. " +
216+
"NOTE: Idle connections that aren't cleaned-up can cause problems if a database is destroyed in " +
217+
"the same plan. This should be > 0 to prevent that.",
218+
ValidateFunc: validateParsableDuration,
219+
},
220+
"connection_max_lifetime": {
221+
Type: schema.TypeString,
222+
Optional: true,
223+
Default: defaultProviderConnectionMaxLifetime.String(),
224+
Description: "Maximum lifetime of any connections to the database. Setting to 0 will hold " +
225+
"connections forever. " +
226+
"NOTE: Idle connections that aren't cleaned-up can cause problems if a database is destroyed in " +
227+
"the same plan. This should be > 0 to prevent that.",
228+
ValidateFunc: validateParsableDuration,
229+
},
195230
"expected_version": {
196231
Type: schema.TypeString,
197232
Optional: true,
@@ -236,6 +271,13 @@ func validateExpectedVersion(v interface{}, key string) (warnings []string, erro
236271
return
237272
}
238273

274+
func validateParsableDuration(v any, _ string) (warnings []string, errors []error) {
275+
if _, err := time.ParseDuration(v.(string)); err != nil {
276+
errors = append(errors, fmt.Errorf("invalid duration (%q): %w", v.(string), err))
277+
}
278+
return
279+
}
280+
239281
func getRDSAuthToken(region string, profile string, role string, username string, host string, port int) (string, error) {
240282
endpoint := fmt.Sprintf("%s:%d", host, port)
241283

@@ -367,6 +409,10 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
367409
password = d.Get("password").(string)
368410
}
369411

412+
// Safe to ignore the error, since this is already checked in validation
413+
connMaxIdleTime, _ := time.ParseDuration(d.Get("connection_max_idle_time").(string))
414+
connMaxLifetime, _ := time.ParseDuration(d.Get("connection_max_lifetime").(string))
415+
370416
config := Config{
371417
Scheme: d.Get("scheme").(string),
372418
Host: host,
@@ -379,6 +425,9 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
379425
ApplicationName: "Terraform provider",
380426
ConnectTimeoutSec: d.Get("connect_timeout").(int),
381427
MaxConns: d.Get("max_connections").(int),
428+
MaxIdleConns: d.Get("max_idle_connections").(int),
429+
ConnMaxIdleTime: connMaxIdleTime,
430+
ConnMaxLifetime: connMaxLifetime,
382431
ExpectedVersion: version,
383432
SSLRootCertPath: d.Get("sslrootcert").(string),
384433
GCPIAMImpersonateServiceAccount: d.Get("gcp_iam_impersonate_service_account").(string),

0 commit comments

Comments
 (0)