Skip to content

Commit a3f930e

Browse files
committed
Replace EC2 API vCPU lookup with static instance size parsing
Drop the aws-sdk-go-v2/service/ec2 dependency entirely. Instead of calling DescribeInstanceTypes (which required ec2:DescribeInstanceTypes IAM permission and added ~5s IMDS timeout in tests), parse vCPU count from the RDS instance class size suffix using a static map of standard AWS size-to-vCPU mappings. Unlisted NUMxlarge sizes are handled via multiplier parsing. https://claude.ai/code/session_01AhnBVCBWjk24T7BBQtmkbq
1 parent c47a270 commit a3f930e

5 files changed

Lines changed: 163 additions & 172 deletions

File tree

engine/go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ require (
99
github.com/aws/aws-sdk-go v1.55.8
1010
github.com/aws/aws-sdk-go-v2 v1.41.5
1111
github.com/aws/aws-sdk-go-v2/config v1.32.14
12-
github.com/aws/aws-sdk-go-v2/service/ec2 v1.297.0
1312
github.com/aws/aws-sdk-go-v2/service/rds v1.117.1
1413
github.com/containerd/errdefs v1.0.0
1514
github.com/docker/cli v28.5.2+incompatible

engine/go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgq
2828
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps=
2929
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw=
3030
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY=
31-
github.com/aws/aws-sdk-go-v2/service/ec2 v1.297.0 h1:A+7NViqbMUCoTQFWjbSXdbzE4K5Ziu2zWJtZzAusm+A=
32-
github.com/aws/aws-sdk-go-v2/service/ec2 v1.297.0/go.mod h1:R+2BNtUfTfhPY0RH18oL02q116bakeBWjanrbnVBqkM=
3331
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=
3432
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
3533
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto=

engine/internal/rdsrefresh/parallelism.go

Lines changed: 70 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,45 @@
55
package rdsrefresh
66

77
import (
8-
"context"
98
"fmt"
109
"runtime"
10+
"strconv"
1111
"strings"
1212

13-
"github.com/aws/aws-sdk-go-v2/aws"
14-
awsconfig "github.com/aws/aws-sdk-go-v2/config"
15-
"github.com/aws/aws-sdk-go-v2/service/ec2"
16-
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
17-
1813
"gitlab.com/postgres-ai/database-lab/v3/pkg/log"
1914
)
2015

2116
const (
22-
// rdsInstanceClassPrefix is stripped to derive the EC2 instance type.
17+
// rdsInstanceClassPrefix is stripped to derive the instance size.
2318
rdsInstanceClassPrefix = "db."
2419

2520
// minParallelJobs is the minimum parallelism level.
2621
minParallelJobs = 1
2722
)
2823

29-
// EC2API defines the interface for EC2 client operations used for vCPU lookup.
30-
type EC2API interface {
31-
DescribeInstanceTypes(ctx context.Context, params *ec2.DescribeInstanceTypesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstanceTypesOutput, error)
24+
// instanceSizeVCPUs maps AWS instance size suffixes to their typical vCPU count.
25+
// this mapping is consistent across most instance families (m5, m6g, r5, r6g, c5, etc.).
26+
// graviton and intel/amd variants of the same size have the same vCPU count.
27+
var instanceSizeVCPUs = map[string]int{
28+
"micro": 1,
29+
"small": 1,
30+
"medium": 2,
31+
"large": 2,
32+
"xlarge": 4,
33+
"2xlarge": 8,
34+
"3xlarge": 12,
35+
"4xlarge": 16,
36+
"6xlarge": 24,
37+
"8xlarge": 32,
38+
"9xlarge": 36,
39+
"10xlarge": 40,
40+
"12xlarge": 48,
41+
"16xlarge": 64,
42+
"18xlarge": 72,
43+
"24xlarge": 96,
44+
"32xlarge": 128,
45+
"48xlarge": 192,
46+
"metal": 96,
3247
}
3348

3449
// ParallelismConfig holds the computed parallelism levels for dump and restore.
@@ -40,8 +55,10 @@ type ParallelismConfig struct {
4055
// ResolveParallelism determines the optimal parallelism levels for pg_dump and pg_restore.
4156
// dump parallelism is based on the vCPU count of the RDS clone instance class.
4257
// restore parallelism is based on the vCPU count of the local machine.
43-
func ResolveParallelism(ctx context.Context, cfg *Config) (*ParallelismConfig, error) {
44-
dumpJobs, err := resolveRDSInstanceVCPUs(ctx, cfg)
58+
// local vCPU detection uses runtime.NumCPU(), which works on Linux
59+
// (the target platform for DBLab Engine).
60+
func ResolveParallelism(cfg *Config) (*ParallelismConfig, error) {
61+
dumpJobs, err := resolveRDSInstanceVCPUs(cfg.RDSClone.InstanceClass)
4562
if err != nil {
4663
return nil, fmt.Errorf("failed to resolve RDS instance vCPUs: %w", err)
4764
}
@@ -56,79 +73,71 @@ func ResolveParallelism(ctx context.Context, cfg *Config) (*ParallelismConfig, e
5673
}, nil
5774
}
5875

59-
// resolveRDSInstanceVCPUs looks up the vCPU count for the configured RDS instance class
60-
// by querying the EC2 DescribeInstanceTypes API.
61-
func resolveRDSInstanceVCPUs(ctx context.Context, cfg *Config) (int, error) {
62-
ec2Client, err := newEC2Client(ctx, cfg)
76+
// resolveRDSInstanceVCPUs estimates the vCPU count for the given RDS instance class
77+
// by parsing the instance size suffix (e.g. "xlarge" from "db.m5.xlarge").
78+
// the mapping covers standard AWS size naming used across RDS instance families.
79+
// if the size is not recognized, it attempts to parse a numeric multiplier prefix
80+
// (e.g. "2xlarge" → 8 vCPUs).
81+
func resolveRDSInstanceVCPUs(instanceClass string) (int, error) {
82+
size, err := extractInstanceSize(instanceClass)
6383
if err != nil {
64-
return 0, fmt.Errorf("failed to create EC2 client: %w", err)
84+
return 0, err
6585
}
6686

67-
return lookupInstanceVCPUs(ctx, ec2Client, cfg.RDSClone.InstanceClass)
68-
}
69-
70-
func newEC2Client(ctx context.Context, cfg *Config) (EC2API, error) {
71-
awsCfg, err := awsconfig.LoadDefaultConfig(ctx, awsconfig.WithRegion(cfg.AWS.Region))
72-
if err != nil {
73-
return nil, fmt.Errorf("failed to load AWS config: %w", err)
87+
if vcpus, ok := instanceSizeVCPUs[size]; ok {
88+
return vcpus, nil
7489
}
7590

76-
var opts []func(*ec2.Options)
77-
if cfg.AWS.Endpoint != "" {
78-
opts = append(opts, func(o *ec2.Options) {
79-
o.BaseEndpoint = aws.String(cfg.AWS.Endpoint)
80-
})
91+
// handle unlisted NUMxlarge sizes by parsing the multiplier
92+
vcpus, err := parseXlargeMultiplier(size)
93+
if err != nil {
94+
return 0, fmt.Errorf("unknown instance size %q in class %q", size, instanceClass)
8195
}
8296

83-
return ec2.NewFromConfig(awsCfg, opts...), nil
97+
return vcpus, nil
8498
}
8599

86-
// lookupInstanceVCPUs queries EC2 for the vCPU count of the given RDS instance class.
87-
func lookupInstanceVCPUs(ctx context.Context, client EC2API, rdsInstanceClass string) (int, error) {
88-
ec2InstanceType, err := rdsClassToEC2Type(rdsInstanceClass)
89-
if err != nil {
90-
return 0, err
100+
// extractInstanceSize extracts the size component from an RDS instance class.
101+
// for example, "db.m5.xlarge" → "xlarge", "db.r6g.2xlarge" → "2xlarge".
102+
func extractInstanceSize(instanceClass string) (string, error) {
103+
if !strings.HasPrefix(instanceClass, rdsInstanceClassPrefix) {
104+
return "", fmt.Errorf("invalid RDS instance class %q: expected %q prefix", instanceClass, rdsInstanceClassPrefix)
91105
}
92106

93-
result, err := client.DescribeInstanceTypes(ctx, &ec2.DescribeInstanceTypesInput{
94-
InstanceTypes: []ec2types.InstanceType{ec2types.InstanceType(ec2InstanceType)},
95-
})
96-
if err != nil {
97-
return 0, fmt.Errorf("failed to describe EC2 instance type %q: %w", ec2InstanceType, err)
98-
}
99-
100-
if len(result.InstanceTypes) == 0 {
101-
return 0, fmt.Errorf("EC2 instance type %q not found", ec2InstanceType)
102-
}
107+
withoutPrefix := strings.TrimPrefix(instanceClass, rdsInstanceClassPrefix)
103108

104-
info := result.InstanceTypes[0]
105-
if info.VCpuInfo == nil || info.VCpuInfo.DefaultVCpus == nil {
106-
return 0, fmt.Errorf("vCPU info not available for instance type %q", ec2InstanceType)
107-
}
109+
// format is "family.size", e.g. "m5.xlarge" or "r6g.2xlarge"
110+
parts := strings.SplitN(withoutPrefix, ".", 2)
108111

109-
vcpus := int(*info.VCpuInfo.DefaultVCpus)
110-
if vcpus < minParallelJobs {
111-
return minParallelJobs, nil
112+
const expectedParts = 2
113+
if len(parts) != expectedParts || parts[1] == "" {
114+
return "", fmt.Errorf("invalid RDS instance class %q: expected format db.<family>.<size>", instanceClass)
112115
}
113116

114-
return vcpus, nil
117+
return parts[1], nil
115118
}
116119

117-
// rdsClassToEC2Type converts an RDS instance class (e.g. "db.m5.xlarge") to an EC2 instance type ("m5.xlarge").
118-
func rdsClassToEC2Type(rdsClass string) (string, error) {
119-
if !strings.HasPrefix(rdsClass, rdsInstanceClassPrefix) {
120-
return "", fmt.Errorf("invalid RDS instance class %q: expected %q prefix", rdsClass, rdsInstanceClassPrefix)
120+
// parseXlargeMultiplier handles NUMxlarge patterns not in the static map.
121+
// for example, "5xlarge" → 5 * 4 = 20 vCPUs.
122+
func parseXlargeMultiplier(size string) (int, error) {
123+
idx := strings.Index(size, "xlarge")
124+
if idx <= 0 {
125+
return 0, fmt.Errorf("not an xlarge variant: %q", size)
121126
}
122127

123-
ec2Type := strings.TrimPrefix(rdsClass, rdsInstanceClassPrefix)
124-
if ec2Type == "" {
125-
return "", fmt.Errorf("invalid RDS instance class %q: empty after removing prefix", rdsClass)
128+
multiplier, err := strconv.Atoi(size[:idx])
129+
if err != nil {
130+
return 0, fmt.Errorf("invalid multiplier in %q: %w", size, err)
126131
}
127132

128-
return ec2Type, nil
133+
const vcpusPerXlarge = 4
134+
135+
return multiplier * vcpusPerXlarge, nil
129136
}
130137

131138
// resolveLocalVCPUs returns the number of logical CPUs available on the local machine.
139+
// uses runtime.NumCPU() which reads from /proc/cpuinfo on Linux
140+
// (the target platform for DBLab Engine).
132141
func resolveLocalVCPUs() int {
133142
cpus := runtime.NumCPU()
134143
if cpus < minParallelJobs {

0 commit comments

Comments
 (0)