Skip to content

Commit 4480f61

Browse files
authored
feat(metrics): record per-snapshot dirty/empty/total bytes at creation (#2649)
Per-snapshot histograms (full/empty/total bytes + ratio %) recorded inside Sandbox.Pause, attributed by file_type (memfile/rootfs), kind (full/empty), and use_case (pause/build), so we can see how FPR/FPH/reclaim affect snapshot composition and tell pause-time samples from template-build samples apart.
1 parent 7de6855 commit 4480f61

7 files changed

Lines changed: 98 additions & 3 deletions

File tree

packages/orchestrator/benchmarks/benchmark_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ func (tc *testContainer) testOneItem(b *testing.B, buildID, kernelVersion, fcVer
386386
KernelVersion: kernelVersion,
387387
FirecrackerVersion: fcVersion,
388388
})
389-
snap, err := sbx.Pause(ctx, templateMetadata)
389+
snap, err := sbx.Pause(ctx, templateMetadata, sandbox.SnapshotUseCasePause)
390390
require.NoError(b, err)
391391
require.NotNil(b, snap)
392392

packages/orchestrator/cmd/resume-build/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,7 @@ func (r *runner) pauseOnce(ctx context.Context, opts pauseOptions, verbose bool)
640640

641641
// Pause and create snapshot
642642
pauseStart := time.Now()
643-
snapshot, err := sbx.Pause(ctx, newMeta)
643+
snapshot, err := sbx.Pause(ctx, newMeta, sandbox.SnapshotUseCasePause)
644644
pauseDur := time.Since(pauseStart)
645645
totalDur := time.Since(t0)
646646

packages/orchestrator/pkg/sandbox/sandbox.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,7 @@ func (s *Sandbox) Shutdown(ctx context.Context) error {
10321032
func (s *Sandbox) Pause(
10331033
ctx context.Context,
10341034
m metadata.Template,
1035+
useCase SnapshotUseCase,
10351036
) (st *Snapshot, e error) {
10361037
ctx, span := tracer.Start(ctx, "sandbox-snapshot")
10371038
defer span.End()
@@ -1092,6 +1093,7 @@ func (s *Sandbox) Pause(
10921093
if err != nil {
10931094
return nil, fmt.Errorf("failed to get memfile metadata: %w", err)
10941095
}
1096+
recordSnapshotDiff(ctx, "memfile", memfileDiffMetadata, originalMemfile.Header(), useCase)
10951097

10961098
// Start POSTPROCESSING
10971099
memfileDiff, memfileDiffHeader, err := pauseProcessMemory(
@@ -1116,6 +1118,7 @@ func (s *Sandbox) Pause(
11161118
closeHook: s.Close,
11171119
},
11181120
s.config.DefaultCacheDir,
1121+
useCase,
11191122
)
11201123
if err != nil {
11211124
return nil, fmt.Errorf("error while post processing: %w", err)
@@ -1200,6 +1203,7 @@ func pauseProcessRootfs(
12001203
originalHeader *header.Header,
12011204
diffCreator DiffCreator,
12021205
cacheDir string,
1206+
useCase SnapshotUseCase,
12031207
) (d build.Diff, h *header.Header, e error) {
12041208
ctx, span := tracer.Start(ctx, "process-rootfs")
12051209
defer span.End()
@@ -1216,6 +1220,7 @@ func pauseProcessRootfs(
12161220
return nil, nil, fmt.Errorf("error creating diff: %w", err)
12171221
}
12181222
telemetry.ReportEvent(ctx, "exported rootfs")
1223+
recordSnapshotDiff(ctx, "rootfs", rootfsDiffMetadata, originalHeader, useCase)
12191224

12201225
rootfsDiff, err := rootfsDiffFile.CloseToDiff(int64(originalHeader.Metadata.BlockSize))
12211226
if err != nil {
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package sandbox
2+
3+
import (
4+
"context"
5+
6+
"go.opentelemetry.io/otel/attribute"
7+
"go.opentelemetry.io/otel/metric"
8+
9+
"github.com/e2b-dev/infra/packages/shared/pkg/storage/header"
10+
"github.com/e2b-dev/infra/packages/shared/pkg/telemetry"
11+
"github.com/e2b-dev/infra/packages/shared/pkg/utils"
12+
)
13+
14+
var (
15+
snapshotDiffBytes = utils.Must(telemetry.GetHistogram(meter, telemetry.SnapshotDiffBytes))
16+
snapshotDiffRatioBp = utils.Must(telemetry.GetHistogram(meter, telemetry.SnapshotDiffRatioBp))
17+
snapshotTotalBytes = utils.Must(telemetry.GetHistogram(meter, telemetry.SnapshotTotalBytes))
18+
)
19+
20+
type SnapshotUseCase string
21+
22+
const (
23+
SnapshotUseCasePause SnapshotUseCase = "pause"
24+
SnapshotUseCaseBuild SnapshotUseCase = "build"
25+
)
26+
27+
func recordSnapshotDiff(
28+
ctx context.Context,
29+
fileType string,
30+
dm *header.DiffMetadata,
31+
original *header.Header,
32+
useCase SnapshotUseCase,
33+
) {
34+
if dm == nil || original == nil || original.Metadata == nil {
35+
return
36+
}
37+
bs := int64(original.Metadata.BlockSize)
38+
total := int64(original.Metadata.Size)
39+
40+
ft := attribute.String("file_type", fileType)
41+
uc := attribute.String("use_case", string(useCase))
42+
43+
snapshotTotalBytes.Record(ctx, total, metric.WithAttributes(ft, uc))
44+
45+
var dirtyBytes, emptyBytes int64
46+
if dm.Dirty != nil {
47+
dirtyBytes = int64(dm.Dirty.GetCardinality()) * bs
48+
}
49+
if dm.Empty != nil {
50+
emptyBytes = int64(dm.Empty.GetCardinality()) * bs
51+
}
52+
for kind, b := range map[string]int64{
53+
"dirty": dirtyBytes,
54+
"empty": emptyBytes,
55+
} {
56+
attrs := metric.WithAttributes(ft, attribute.String("kind", kind), uc)
57+
snapshotDiffBytes.Record(ctx, b, attrs)
58+
snapshotDiffRatioBp.Record(ctx, ratioBp(b, total), attrs)
59+
}
60+
}
61+
62+
// ratioBp returns num/denom in basis points (10000 = 100.00%) so we keep
63+
// sub-percent resolution. Grafana panels divide by 100 to display percent.
64+
func ratioBp(num, denom int64) int64 {
65+
if denom <= 0 {
66+
return 0
67+
}
68+
bp := num * 10000 / denom
69+
if bp < 0 {
70+
return 0
71+
}
72+
if bp > 10000 {
73+
return 10000
74+
}
75+
76+
return bp
77+
}

packages/orchestrator/pkg/server/sandboxes.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -746,7 +746,7 @@ func (s *Server) snapshotAndCacheSandbox(
746746
FirecrackerVersion: sbx.Config.FirecrackerConfig.FirecrackerVersion,
747747
})
748748

749-
snapshot, err := sbx.Pause(ctx, meta)
749+
snapshot, err := sbx.Pause(ctx, meta, sandbox.SnapshotUseCasePause)
750750
if err != nil {
751751
return nil, fmt.Errorf("error snapshotting sandbox: %w", err)
752752
}

packages/orchestrator/pkg/template/build/layer/layer_executor.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ func (lb *LayerExecutor) PauseAndUpload(
260260
snapshot, err := sbx.Pause(
261261
ctx,
262262
meta,
263+
sandbox.SnapshotUseCaseBuild,
263264
)
264265
if err != nil {
265266
return fmt.Errorf("error processing vm: %w", err)

packages/shared/pkg/telemetry/meters.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ const (
121121
SandboxFCBlockRateLimiterEventCount HistogramType = "orchestrator.sandbox.fc.block.rate_limiter_event_count"
122122
SandboxFCBlockIOEngineThrottled HistogramType = "orchestrator.sandbox.fc.block.io_engine_throttled"
123123
SandboxFCBlockRemainingReqs HistogramType = "orchestrator.sandbox.fc.block.remaining_reqs"
124+
125+
SnapshotDiffBytes HistogramType = "orchestrator.sandbox.snapshot.diff.bytes"
126+
SnapshotDiffRatioBp HistogramType = "orchestrator.sandbox.snapshot.diff.ratio_bp"
127+
SnapshotTotalBytes HistogramType = "orchestrator.sandbox.snapshot.total.bytes"
124128
)
125129

126130
const (
@@ -352,6 +356,10 @@ var histogramDesc = map[HistogramType]string{
352356
SandboxFCBlockRateLimiterEventCount: "Distribution of Firecracker VMM block rate limiter events per metrics flush",
353357
SandboxFCBlockIOEngineThrottled: "Distribution of Firecracker VMM block ops throttled by io_uring engine per metrics flush",
354358
SandboxFCBlockRemainingReqs: "Distribution of Firecracker VMM block queue remaining-request events per metrics flush",
359+
360+
SnapshotDiffBytes: "Per-snapshot dirty/empty bytes per file",
361+
SnapshotDiffRatioBp: "Per-snapshot dirty/empty as fraction of total mapped size, in basis points (10000=100%)",
362+
SnapshotTotalBytes: "Per-snapshot total mapped size of the file",
355363
}
356364

357365
var histogramUnits = map[HistogramType]string{
@@ -382,6 +390,10 @@ var histogramUnits = map[HistogramType]string{
382390
SandboxFCBlockRateLimiterEventCount: "{event}",
383391
SandboxFCBlockIOEngineThrottled: "{op}",
384392
SandboxFCBlockRemainingReqs: "{event}",
393+
394+
SnapshotDiffBytes: "{By}",
395+
SnapshotDiffRatioBp: "{1}",
396+
SnapshotTotalBytes: "{By}",
385397
}
386398

387399
func GetHistogram(meter metric.Meter, name HistogramType) (metric.Int64Histogram, error) {

0 commit comments

Comments
 (0)