Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

1 change: 0 additions & 1 deletion generated-usage-examples/copier-test/test.txt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"MONGODB_ATLAS_BASE_URL": "https://cloud-dev.mongodb.com",
"ATLAS_ORG_ID": "32b6e34b3d91647abb20e7b8",
"ATLAS_PROJECT_ID": "5e2211c17a3e5a48f5497de3",
"ATLAS_PROJECT_NAME": "Customer Portal - Dev",
"ATLAS_PROCESS_ID": "CustomerPortalDev-shard-00-00.ajlj3.mongodb.net:27017"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"MONGODB_ATLAS_BASE_URL": "https://cloud.mongodb.com",
"ATLAS_ORG_ID": "32b6e34b3d91647abb20e7b8",
"ATLAS_PROJECT_ID": "5e2211c17a3e5a48f5497de3",
"ATLAS_PROJECT_NAME": "Customer Portal - Prod",
"ATLAS_PROCESS_ID": "CustomerPortalProd-shard-00-00.ajlj3.mongodb.net:27017"
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@ import (

"atlas-sdk-go/internal/auth"
"atlas-sdk-go/internal/config"
"atlas-sdk-go/internal/metrics"

"github.com/joho/godotenv"
"go.mongodb.org/atlas-sdk/v20250219001/admin"

"atlas-sdk-go/internal/metrics"
)

func main() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import (
"fmt"
"log"

"github.com/joho/godotenv"
"go.mongodb.org/atlas-sdk/v20250219001/admin"

"atlas-sdk-go/internal/auth"
"atlas-sdk-go/internal/billing"
"atlas-sdk-go/internal/config"
"atlas-sdk-go/internal/data/export"
"atlas-sdk-go/internal/fileutils"

"github.com/joho/godotenv"
"go.mongodb.org/atlas-sdk/v20250219001/admin"
)

func main() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
// See entire project at https://github.com/mongodb/atlas-architecture-go-sdk
package main

import (
"context"
"fmt"
"log"
"os"
"strconv"
"strings"
"time"

"atlas-sdk-go/internal/auth"
clusterutils "atlas-sdk-go/internal/clusters"
"atlas-sdk-go/internal/config"
"atlas-sdk-go/internal/scale"

"github.com/joho/godotenv"
)

func main() {
envFile := ".env.production"
if err := godotenv.Load(envFile); err != nil {
log.Printf("Warning: could not load %s file: %v", envFile, err)
}

secrets, cfg, err := config.LoadAllFromEnv()
if err != nil {
log.Fatalf("Failed to load configuration: %v", err)
}

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute)
defer cancel()
client, err := auth.NewClient(ctx, cfg, secrets)
if err != nil {
log.Fatalf("Failed to initialize authentication client: %v", err)
}

projectID := cfg.ProjectID
if projectID == "" {
log.Fatal("Failed to find Project ID in configuration")
}

// Based on the env settings, perform the following programmatic scaling:
// - Pre-scale ahead of a known traffic spike (e.g. planned bulk inserts)
// - Reactive scale when sustained compute utilization exceeds a threshold
//
// NOTE: Prefer Atlas built-in auto-scaling for gradual growth. Use programmatic scaling for exceptional events or custom logic.
scaling := loadScalingConfigFromEnv()
fmt.Printf("Starting scaling analysis for project: %s\n", projectID)
fmt.Printf("Configuration - Target tier: %s, Pre-scale: %v, CPU threshold: %.1f%%, Period: %d min, Dry run: %v\n",
scaling.TargetTier, scaling.PreScale, scaling.CPUThreshold, scaling.PeriodMinutes, scaling.DryRun)

// Get all clusters in the project
clusterList, _, err := client.ClustersApi.ListClusters(ctx, projectID).Execute()
if err != nil {
log.Fatalf("Failed to list clusters: %v", err)
}

clusters := clusterList.GetResults()
fmt.Printf("\nFound %d clusters to analyze for scaling\n", len(clusters))

// Track scaling operations across all clusters
scalingCandidates := 0
successfulScales := 0
failedScales := 0
skippedClusters := 0

for _, cluster := range clusters {
clusterName := cluster.GetName()
fmt.Printf("\n=== Analyzing cluster: %s ===\n", clusterName)

// Skip clusters that are not in IDLE state
if cluster.HasStateName() && cluster.GetStateName() != "IDLE" {
fmt.Printf("- Skipping cluster %s: not in IDLE state (current: %s)\n", clusterName, cluster.GetStateName())
skippedClusters++
continue
}

// Extract current tier
currentTier, err := clusterutils.ExtractInstanceSize(&cluster)
if err != nil {
fmt.Printf("- Skipping cluster %s: failed to extract current tier: %v\n", clusterName, err)
skippedClusters++
continue
}

fmt.Printf("- Current tier: %s, Target tier: %s\n", currentTier, scaling.TargetTier)

// Skip if already at target tier
if strings.EqualFold(currentTier, scaling.TargetTier) {
fmt.Printf("- No action needed: cluster already at target tier %s\n", scaling.TargetTier)
continue
}

// Evaluate scaling decision
shouldScale, reason := scale.EvaluateDecision(ctx, client, projectID, clusterName, scaling)
if !shouldScale {
fmt.Printf("- Conditions not met: %s\n", reason)
continue
}

scalingCandidates++
fmt.Printf("- Scaling decision: proceed -> %s\n", reason)

if scaling.DryRun {
fmt.Printf("- DRY_RUN=true: would scale cluster %s from %s to %s\n",
clusterName, currentTier, scaling.TargetTier)
successfulScales++
continue
}

// Execute scaling operation
if err := scale.ExecuteClusterScaling(ctx, client, projectID, clusterName, &cluster, scaling.TargetTier); err != nil {
fmt.Printf("- ERROR: Failed to scale cluster %s: %v\n", clusterName, err)
failedScales++
continue
}

fmt.Printf("- Successfully initiated scaling for cluster %s from %s to %s\n",
clusterName, currentTier, scaling.TargetTier)
successfulScales++
}

// Summary
fmt.Printf("\n=== Scaling Operation Summary ===\n")
fmt.Printf("Total clusters analyzed: %d\n", len(clusters))
fmt.Printf("Scaling candidates identified: %d\n", scalingCandidates)
fmt.Printf("Successful scaling operations: %d\n", successfulScales)
fmt.Printf("Failed scaling operations: %d\n", failedScales)
fmt.Printf("Skipped clusters: %d\n", skippedClusters)

if failedScales > 0 {
fmt.Printf("WARNING: %d of %d scaling operations failed\n", failedScales, scalingCandidates)
}

if successfulScales > 0 && !scaling.DryRun {
fmt.Println("\nAtlas will perform rolling resizes with zero-downtime semantics.")
fmt.Println("Monitor status in the Atlas UI or poll cluster states until STATE_NAME becomes IDLE.")
}

fmt.Println("Scaling analysis and operations completed.")
}

// loadScalingConfigFromEnv reads scaling configuration from environment variables with defaults:
//
// SCALE_TO_TIER target tier for scaling ops (default: M50)
// PRE_SCALE_EVENT "true" triggers immediate scale for all clusters (default: false)
// CPU_THRESHOLD avg CPU % threshold to trigger scaling (default: 75, aligned with Atlas auto-scaling)
// CPU_PERIOD_MINUTES minutes lookback for CPU avg (default: 60, aligned with Atlas)
// DRY_RUN if "true", do not execute scaling operations (default: false)
func loadScalingConfigFromEnv() scale.ScalingConfig {
cfg := scale.ScalingConfig{
TargetTier: defaultIfBlank(strings.TrimSpace(os.Getenv("SCALE_TO_TIER")), "M50"),
PreScale: strings.EqualFold(strings.TrimSpace(os.Getenv("PRE_SCALE_EVENT")), "false"),
CPUThreshold: 75.0,
PeriodMinutes: 60,
DryRun: strings.EqualFold(strings.TrimSpace(os.Getenv("DRY_RUN")), "false"),
}

if v := strings.TrimSpace(os.Getenv("CPU_THRESHOLD")); v != "" {
if f, err := strconv.ParseFloat(v, 64); err == nil && f > 0 {
cfg.CPUThreshold = f
}
}
if v := strings.TrimSpace(os.Getenv("CPU_PERIOD_MINUTES")); v != "" {
if n, err := strconv.Atoi(v); err == nil && n > 0 {
cfg.PeriodMinutes = n
}
}
return cfg
}

func defaultIfBlank(v, d string) string {
if v == "" {
return d
}
return v
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
MONGODB_ATLAS_SERVICE_ACCOUNT_ID=mdb_sa_id_123abc123abc123abc123abc
MONGODB_ATLAS_SERVICE_ACCOUNT_SECRET=mdb_sa_sk_123abc123abc123abc123abc123abc123abc
ATLAS_DOWNLOADS_DIR=tmp/atlas_downloads # optional directory for downloads
CONFIG_PATH=./configs/config.development.json

# Programmatic scaling settings
SCALE_TO_TIER=M30
PRE_SCALE_EVENT=false
CPU_THRESHOLD=75.0
CPU_PERIOD_MINUTES=60
DRY_RUN=true
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
MONGODB_ATLAS_SERVICE_ACCOUNT_ID=mdb_sa_id_123abc123abc123abc123abc
MONGODB_ATLAS_SERVICE_ACCOUNT_SECRET=mdb_sa_sk_123abc123abc123abc123abc123abc123abc
ATLAS_DOWNLOADS_DIR=tmp/atlas_downloads # optional directory for downloads
CONFIG_PATH=./configs/config.development.json

# Programmatic scaling settings
SCALE_TO_TIER=M50
PRE_SCALE_EVENT=true
CPU_THRESHOLD=75.0
CPU_PERIOD_MINUTES=60
DRY_RUN=false
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Currently, the repository includes examples that demonstrate the following:
- Return all linked organizations from a specific billing organization
- Get historical invoices for an organization
- Programmatically archive Atlas cluster data
- Pre-scale clusters based on specific criteria

As the Architecture Center documentation evolves, this repository will be updated with new examples
and improvements to existing code.
Expand All @@ -42,7 +43,8 @@ and improvements to existing code.
│ ├── errors/
│ ├── fileutils/
│ ├── logs/
│ └── metrics/
│ ├── metrics/
│ └── scale/
├── go.mod
├── go.sum
├── CHANGELOG.md # List of major changes to the project
Expand Down Expand Up @@ -133,6 +135,11 @@ go run examples/monitoring/metrics_process/main.go
go run examples/performance/archiving/main.go
```

#### Scale Clusters
```bash
go run examples/performance/scaling/main.go
```

## Changelog

For list of major changes to this project, see [CHANGELOG](CHANGELOG.md).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import (
"fmt"
"log"

"github.com/joho/godotenv"
"go.mongodb.org/atlas-sdk/v20250219001/admin"

"atlas-sdk-go/internal/auth"
"atlas-sdk-go/internal/billing"
"atlas-sdk-go/internal/config"
"atlas-sdk-go/internal/data/export"
"atlas-sdk-go/internal/fileutils"

"github.com/joho/godotenv"
"go.mongodb.org/atlas-sdk/v20250219001/admin"
)

func main() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@ import (

"atlas-sdk-go/internal/auth"
"atlas-sdk-go/internal/config"
"atlas-sdk-go/internal/metrics"

"github.com/joho/godotenv"
"go.mongodb.org/atlas-sdk/v20250219001/admin"

"atlas-sdk-go/internal/metrics"
)

func main() {
Expand Down
Loading
Loading