Skip to content

feat: add replica count to scale mode, gate resource throttling on idle.replicas >= 1#909

Merged
acouvreur merged 4 commits into
mainfrom
add-scaling-replicas
May 14, 2026
Merged

feat: add replica count to scale mode, gate resource throttling on idle.replicas >= 1#909
acouvreur merged 4 commits into
mainfrom
add-scaling-replicas

Conversation

@acouvreur

@acouvreur acouvreur commented May 14, 2026

Copy link
Copy Markdown
Member

Summary

Extends scale mode with a first-class replica count, making it possible to scale workloads to any replica count on session start/stop — not just toggle CPU/memory limits.

Closes #28
Closes #258

New labels

Label Default Description
sablier.idle.replicas 0 Replica count when idle. 0 stops the workload (default). ≥ 1 keeps it running with optional resource throttling.
sablier.active.replicas 1 Replica count when a session is requested.

Behavior change

Resource throttling (sablier.idle.cpu / sablier.idle.memory) now only activates when sablier.idle.replicas >= 1.

Previously, adding CPU/memory labels alone was enough. Now you must also set sablier.idle.replicas=1 explicitly.

Migration: add sablier.idle.replicas=1 to any workload that used sablier.idle.cpu or sablier.idle.memory.

Scaling to more than 1 replica

labels:
  - "sablier.enable=true"
  - "sablier.group=myapp"
  - "sablier.idle.replicas=1"    # keep 1 replica alive when idle
  - "sablier.idle.cpu=0.1"
  - "sablier.idle.memory=64m"
  - "sablier.active.replicas=3"  # scale up to 3 on demand
  - "sablier.active.cpu=2.0"
  - "sablier.active.memory=512m"

Replicas-only mode (no CPU/memory throttling) is also supported:

labels:
  - "sablier.idle.replicas=1"
  - "sablier.active.replicas=3"

Provider changes

  • Docker / Podman: ContainerUpdate is skipped when no CPU/memory labels are set (replicas only)
  • Docker Swarm: ServiceUpdateScale replaces the separate replica + resource update calls — one ServiceUpdate round-trip. Fixes nil-pointer panic and missing memory parsing when CPU label is absent
  • Kubernetes: scale() then optional scaleResources() called sequentially; resources step is skipped when no CPU/memory labels are present

Tests added

  • Unit tests for new sablier.idle.replicas / sablier.active.replicas label parsing
  • Integration tests for replicas-only scale mode (no CPU/memory) on Docker, Swarm, and Kubernetes
  • Integration tests for 2-replica scale mode on Swarm and Kubernetes

Documentation

  • Updated docs/configuration.md: new label table rows, updated scale mode section, added Docker Swarm example
  • Updated examples/scale-mode/README.md: documents the breaking change, shows multi-replica usage, replicas-only usage

…le.replicas >= 1

Scale mode now has two distinct behaviors controlled by sablier.idle.replicas:

- idle.replicas=0 (default): workload is stopped on session expiry (same as
  the default stop behavior). CPU/memory labels are ignored.
- idle.replicas>=1: workload keeps running at the given replica count.
  CPU/memory throttling is applied when both idle/active resource labels are set.

New labels:
  sablier.idle.replicas  - replica count when idle (default 0, stops workload)
  sablier.active.replicas - replica count when active (default 1)

Providers updated:
  - Docker/Podman: skip ContainerUpdate when no CPU/memory labels are set
  - Docker Swarm: ServiceUpdateScale combines replica + resource update in a
    single ServiceUpdate call; fixes nil-pointer panic and missing memory
    parsing when only replicas are set
  - Kubernetes: scale() then scaleResources() called sequentially; scaleResources
    is skipped when no CPU/memory labels are present

BREAKING CHANGE: sablier.idle.cpu / sablier.idle.memory no longer enable
throttling on their own. Add sablier.idle.replicas=1 to existing workloads
that relied on CPU/memory throttling.

Co-authored-by: GitHub Copilot <copilot@github.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation provider Issue related to a provider labels May 14, 2026
@github-actions

github-actions Bot commented May 14, 2026

Copy link
Copy Markdown

Test Results

✅ All tests passed! | 413 tests in 157.633s

View HTML Test Report

@github-actions

github-actions Bot commented May 14, 2026

Copy link
Copy Markdown
┌────────────────────────────────────────────────────────────────────────────────┐
│ Diff between sablier and sablier                                               │
├─────────┬──────────────────────────────────────┬──────────┬──────────┬─────────┤
│ PERCENT │ NAME                                 │ OLD SIZE │ NEW SIZE │ DIFF    │
├─────────┼──────────────────────────────────────┼──────────┼──────────┼─────────┤
│ +28.02% │ github.com/sablierapp/sablier        │ 325 kB   │ 416 kB   │ +91 kB  │
│ +0.03%  │ k8s.io/client-go                     │ 14 MB    │ 14 MB    │ +3.9 kB │
│ +1.69%  │ time                                 │ 157 kB   │ 160 kB   │ +2.7 kB │
│ +0.21%  │ <autogenerated>                      │ 948 kB   │ 950 kB   │ +2.0 kB │
│ +0.58%  │ go.yaml.in/yaml/v3                   │ 312 kB   │ 314 kB   │ +1.8 kB │
│ +46.44% │ github.com/docker/go-units           │ 3.3 kB   │ 4.8 kB   │ +1.5 kB │
│ +0.02%  │ runtime                              │ 3.2 MB   │ 3.2 MB   │ +539 B  │
│ +0.13%  │ text/template                        │ 292 kB   │ 292 kB   │ +389 B  │
│ +0.29%  │ vendor/golang.org/x/net/http2/hpack  │ 35 kB    │ 35 kB    │ +102 B  │
│ +0.03%  │ sigs.k8s.io/json                     │ 173 kB   │ 173 kB   │ +54 B   │
│ +0.11%  │ vendor/golang.org/x/net/idna         │ 22 kB    │ 22 kB    │ +25 B   │
│ +0.31%  │ internal/cpu                         │ 6.1 kB   │ 6.1 kB   │ +19 B   │
│ +0.00%  │ go.opentelemetry.io/otel             │ 407 kB   │ 407 kB   │ +17 B   │
│ +0.00%  │ k8s.io/kube-openapi                  │ 466 kB   │ 466 kB   │ +17 B   │
│ +0.01%  │ log                                  │ 126 kB   │ 126 kB   │ +16 B   │
│ +0.00%  │ k8s.io/apimachinery                  │ 1.8 MB   │ 1.8 MB   │ +9 B    │
│ +0.00%  │ github.com/moby/moby/api             │ 149 kB   │ 149 kB   │ +6 B    │
│ +0.00%  │ github.com/gin-gonic/gin             │ 336 kB   │ 336 kB   │ +4 B    │
│ +0.00%  │ github.com/quic-go/quic-go           │ 1.3 MB   │ 1.3 MB   │ +4 B    │
│ +0.00%  │ k8s.io/klog/v2                       │ 124 kB   │ 124 kB   │ +2 B    │
│ +0.01%  │ github.com/pmezard/go-difflib        │ 17 kB    │ 17 kB    │ +1 B    │
│ -0.00%  │ html                                 │ 136 kB   │ 136 kB   │ -2 B    │
│ -0.00%  │ github.com/spf13/pflag               │ 302 kB   │ 302 kB   │ -2 B    │
│ -0.00%  │ os                                   │ 210 kB   │ 210 kB   │ -2 B    │
│ -0.01%  │ sort                                 │ 29 kB    │ 29 kB    │ -2 B    │
│ -0.00%  │ go.mongodb.org/mongo-driver/v2       │ 672 kB   │ 672 kB   │ -2 B    │
│ -0.00%  │ google.golang.org/protobuf           │ 1.7 MB   │ 1.7 MB   │ -4 B    │
│ -0.01%  │ github.com/prometheus/common         │ 68 kB    │ 68 kB    │ -4 B    │
│ -0.01%  │ unique                               │ 34 kB    │ 34 kB    │ -4 B    │
│ -0.05%  │ embed                                │ 12 kB    │ 12 kB    │ -6 B    │
│ -0.00%  │ sigs.k8s.io/structured-merge-diff/v6 │ 275 kB   │ 275 kB   │ -6 B    │
│ -0.00%  │ k8s.io/api                           │ 17 MB    │ 17 MB    │ -6 B    │
│ -0.00%  │ reflect                              │ 340 kB   │ 340 kB   │ -8 B    │
│ -0.01%  │ golang.org/x/text                    │ 162 kB   │ 162 kB   │ -9 B    │
│ -0.06%  │ github.com/spf13/afero               │ 21 kB    │ 21 kB    │ -12 B   │
│ -0.00%  │ net                                  │ 1.7 MB   │ 1.7 MB   │ -13 B   │
│ -0.05%  │ k8s.io/utils                         │ 32 kB    │ 32 kB    │ -17 B   │
│ -0.30%  │ vendor/golang.org/x/sys/cpu          │ 6.4 kB   │ 6.4 kB   │ -19 B   │
│ -0.03%  │ go.opentelemetry.io/auto/sdk         │ 89 kB    │ 89 kB    │ -23 B   │
│ -0.01%  │ go.yaml.in/yaml/v2                   │ 275 kB   │ 275 kB   │ -32 B   │
│ -0.00%  │ crypto                               │ 1.9 MB   │ 1.9 MB   │ -53 B   │
│ -0.04%  │ encoding/json                        │ 172 kB   │ 172 kB   │ -71 B   │
│ -0.02%  │ golang.org/x/net                     │ 789 kB   │ 789 kB   │ -129 B  │
│ -0.58%  │ gopkg.in/yaml.v3                     │ 305 kB   │ 304 kB   │ -1.8 kB │
├─────────┼──────────────────────────────────────┼──────────┼──────────┼─────────┤
│ +19.23% │ .rodata                              │ 2.1 MB   │ 2.5 MB   │ +410 kB │
│ +0.01%  │ .noptrdata                           │ 451 kB   │ 451 kB   │ +32 B   │
├─────────┼──────────────────────────────────────┼──────────┼──────────┼─────────┤
│ +0.88%  │ sablier                              │ 59 MB    │ 59 MB    │ +516 kB │
│         │ sablier                              │          │          │         │
└─────────┴──────────────────────────────────────┴──────────┴──────────┴─────────┘

@github-actions github-actions Bot added the ci label May 14, 2026
@sonarqubecloud

Copy link
Copy Markdown

@acouvreur acouvreur merged commit 5722c6f into main May 14, 2026
8 checks passed
@acouvreur acouvreur deleted the add-scaling-replicas branch May 14, 2026 23:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci documentation Improvements or additions to documentation provider Issue related to a provider

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Always scales back up to 1 replica on Kubernetes Configure scale number for Swarm service

1 participant