Skip to content

feat(daemon,sdk,telemetry): add sandbox memory cache metric and Analy… - #5119

Open
MDzaja wants to merge 1 commit into
feat/sandbox-live-metricsfrom
feat/sandbox-historical-metrics
Open

feat(daemon,sdk,telemetry): add sandbox memory cache metric and Analy…#5119
MDzaja wants to merge 1 commit into
feat/sandbox-live-metricsfrom
feat/sandbox-historical-metrics

Conversation

@MDzaja

@MDzaja MDzaja commented Jun 23, 2026

Copy link
Copy Markdown
Member

Summary

Expands per-sandbox telemetry coverage:

  1. Adds a new daytona.sandbox.memory.cache gauge (page cache, bytes) to the metrics emitted by the sandbox daemon.
  2. Generates Analytics API clients for Go, Python (sync + async), Ruby, and Java — a TypeScript client already existed — so every SDK language can consume the analytics telemetry/usage API.

Changes

New metric — daytona.sandbox.memory.cache

  • libs/common-go/pkg/telemetry/metrics.go — register the gauge in registerUsageMetrics as an Int64ObservableGauge, observed from the existing ReadCgroupMemCacheBytes(cgroupV2) helper (already used by the live system-metrics endpoint; no new cgroup logic).
  • apps/dashboard/src/constants/metrics.ts — display name "Memory Cache".
  • apps/docs/.../observability/otel-collection.mdx — document the metric.

Analytics API clients (Go / Python / Python-async / Ruby / Java)

  • New generated libraries under libs/analytics-api-client-{go,python,python-async,ruby,java}, each with a generate:api-client target that reuses the shared hack/<lang>-client/ post-processing scripts and templates (no duplicated tooling).
  • Workspace / dependency wiring: go.work, root pyproject.toml (+ poetry.lock), Gemfile, package.json (excluded from the default API-spec generation run, consistent with the existing TypeScript analytics/billing clients).
  • Publishing wiring: release.yaml (Go module tag) and sdk_publish.yaml (publish matrix entries for the Python/Ruby/Java packages).

Why it's safe

  • The metric is purely additive — a new gauge alongside existing series; it cannot affect existing metrics or schemas. On a cgroup read error the observation is skipped (never crashes the daemon); a registration error is logged and startup continues. One bounded read/parse of the small kernel memory.stat per sampling tick — no OOM, no new shared state, no new race.
  • The generated clients are not imported by any SDK or service yet — no runtime impact; the only surface to watch is CI/build/package resolution from the workspace/dependency edits.

Verification

  • go build / go vet / go test ./pkg/telemetry/... clean.
  • New metric emitted end-to-end into a collector → ClickHouse (stored, correct value) and into a local Prometheus (otel_daytona_sandbox_memory_cache_bytes, real value observed).
  • The downstream metrics query returns metric names generically (no allowlist), so the new series surfaces automatically once emitted.
  • All five generated clients build and were exercised against the live analytics API (each retrieved real metric series); nx discovers all new projects; poetry check valid; workflows validated.

Rollout

  • The metric is emitted by the sandbox daemon, so it applies to sandboxes created after the daemon rolls out (existing sandboxes keep the previous daemon until recreated).
  • Recommended: canary one runner first — create a sandbox and confirm daytona.sandbox.memory.cache appears via the telemetry API — then roll out fleet-wide.
  • No collector/infra config change needed (the new gauge is handled generically). Storage impact is ~one extra gauge series per sandbox.

Summary by cubic

Adds a new sandbox memory cache metric and ships Analytics API client libraries for Go, Python (sync and async), Ruby, and Java to let all SDKs query telemetry and usage.

  • New Features

    • Telemetry: emit daytona.sandbox.memory.cache (bytes) using the existing cgroup reader; adds Dashboard label “Memory Cache” and docs entry.
    • SDKs: add Analytics API clients analytics-api-client-go, analytics-api-client-python, analytics-api-client-python-async, analytics-api-client-ruby, analytics-api-client-java.
  • Dependencies

    • CI/release: tag libs/analytics-api-client-go in release.yaml; add Python/Ruby/Java analytics clients to the sdk_publish.yaml matrix.
    • Workspace: include ./libs/analytics-api-client-go in go.work; add daytona_analytics_api_client to the Gemfile.

Written for commit b4a0f88. Summary will update on new commits.

Review in cubic

…tics API clients

Signed-off-by: MDzaja <mirkodzaja0@gmail.com>
@MDzaja
MDzaja requested review from a team and Tpuljak as code owners June 23, 2026 12:58
@nx-cloud

nx-cloud Bot commented Jun 23, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit b4a0f88

Command Status Duration Result
nx run-many --target=docker --configuration=pr-... ❌ Failed 10m 2s View ↗

💡 Dealing with memory or CPU issues? See memory and CPU details with the resource usage add-on ↗.


☁️ Nx Cloud last updated this comment at 2026-06-23 13:10:49 UTC

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

9 issues found across 177 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="libs/analytics-api-client-python/setup.py">

<violation number="1" location="libs/analytics-api-client-python/setup.py:35">
P2: Author/email set to OpenAPI Generator community placeholder instead of Daytona Platforms Inc. This is a first-party publishable package — metadata should match the existing Python client convention so published package attribution is correct.</violation>

<violation number="2" location="libs/analytics-api-client-python/setup.py:42">
P2: Missing `license="Apache-2.0"` in setup(). The existing first-party Python client specifies this. Without it, published package metadata omits the license, which is a quality gap for a publishable package.</violation>
</file>

<file name="libs/analytics-api-client-java/src/main/java/io/daytona/analytics/api/client/ApiResponse.java">

<violation number="1" location="libs/analytics-api-client-java/src/main/java/io/daytona/analytics/api/client/ApiResponse.java:42">
P3: Javadoc typo: "response bod" should be "response body".</violation>
</file>

<file name="libs/analytics-api-client-python/.gitignore">

<violation number="1" location="libs/analytics-api-client-python/.gitignore:45">
P3: Typo in gitignore pattern: `*,cover` should be `*.cover` (dot instead of comma). The current pattern never matches any real coverage output file.</violation>
</file>

<file name="libs/analytics-api-client-python/daytona_analytics_api_client/models/models_usage_period.py">

<violation number="1" location="libs/analytics-api-client-python/daytona_analytics_api_client/models/models_usage_period.py:23">
P3: `Optional` is imported twice (lines 16 and 19). Merge `Set` into the first import and drop the second line to eliminate the flake8 F811 violation.</violation>
</file>

<file name="libs/analytics-api-client-ruby/.rubocop.yml">

<violation number="1" location="libs/analytics-api-client-ruby/.rubocop.yml:4">
P2: RuboCop `TargetRubyVersion` is set to `2.4` but the gemspec requires `>= 2.7`. This overrides auto-detection and prevents RuboCop from applying cops relevant to 2.7+ features, while keeping obsolete 2.4-specific rules active.</violation>
</file>

<file name="libs/analytics-api-client-go/client.go">

<violation number="1" location="libs/analytics-api-client-go/client.go:216">
P3: Float64 query parameters are formatted with 32‑bit precision, which truncates float64 values when they’re added to query strings. Split the float32 and float64 cases so float64 uses 64‑bit formatting.</violation>
</file>

<file name="libs/analytics-api-client-python-async/tox.ini">

<violation number="1" location="libs/analytics-api-client-python-async/tox.ini:9">
P2: The tox test command is configured to run pytest even though this package has no tests, which makes tox fail by default with pytest exit code 5 (no tests collected).</violation>
</file>

<file name="libs/analytics-api-client-java/src/main/java/io/daytona/analytics/api/client/auth/HttpBearerAuth.java">

<violation number="1" location="libs/analytics-api-client-java/src/main/java/io/daytona/analytics/api/client/auth/HttpBearerAuth.java:40">
P2: HttpBearerAuth.getBearerToken() throws NullPointerException when called before tokenSupplier is set.

The field `tokenSupplier` is never initialized (defaults to null), so calling `getBearerToken()` before `setBearerToken(...)` will crash with NPE. The `applyToParams` method in the same class handles a null supplier safely via `Optional.ofNullable`, making this an inconsistency that will surprise callers using the public getter.</violation>
</file>

Note: This PR contains a large number of files. cubic only reviews up to 100 files per PR, so some files may not have been reviewed. cubic prioritizes the most important files to review.
On a pro plan you can use ultrareview for larger PRs.
Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

install_requires=REQUIRES,
packages=find_packages(exclude=["test", "tests"]),
include_package_data=True,
long_description_content_type='text/markdown',

@cubic-dev-ai cubic-dev-ai Bot Jun 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Missing license="Apache-2.0" in setup(). The existing first-party Python client specifies this. Without it, published package metadata omits the license, which is a quality gap for a publishable package.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At libs/analytics-api-client-python/setup.py, line 42:

<comment>Missing `license="Apache-2.0"` in setup(). The existing first-party Python client specifies this. Without it, published package metadata omits the license, which is a quality gap for a publishable package.</comment>

<file context>
@@ -0,0 +1,47 @@
+    install_requires=REQUIRES,
+    packages=find_packages(exclude=["test", "tests"]),
+    include_package_data=True,
+    long_description_content_type='text/markdown',
+    long_description="""\
+    Daytona Analytics API - Read-only telemetry and usage data. Authenticated via Daytona API keys or JWT tokens.
</file context>
Fix with cubic

Comment on lines +35 to +36
author="OpenAPI Generator community",
author_email="team@openapitools.org",

@cubic-dev-ai cubic-dev-ai Bot Jun 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Author/email set to OpenAPI Generator community placeholder instead of Daytona Platforms Inc. This is a first-party publishable package — metadata should match the existing Python client convention so published package attribution is correct.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At libs/analytics-api-client-python/setup.py, line 35:

<comment>Author/email set to OpenAPI Generator community placeholder instead of Daytona Platforms Inc. This is a first-party publishable package — metadata should match the existing Python client convention so published package attribution is correct.</comment>

<file context>
@@ -0,0 +1,47 @@
+    name=NAME,
+    version=VERSION,
+    description="Daytona Analytics API",
+    author="OpenAPI Generator community",
+    author_email="team@openapitools.org",
+    url="",
</file context>
Suggested change
author="OpenAPI Generator community",
author_email="team@openapitools.org",
author="Daytona Platforms Inc.",
author_email="support@daytona.com",
Fix with cubic

# This file is based on https://github.com/rails/rails/blob/master/.rubocop.yml (MIT license)
# Automatically generated by OpenAPI Generator (https://openapi-generator.tech)
AllCops:
TargetRubyVersion: 2.4

@cubic-dev-ai cubic-dev-ai Bot Jun 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: RuboCop TargetRubyVersion is set to 2.4 but the gemspec requires >= 2.7. This overrides auto-detection and prevents RuboCop from applying cops relevant to 2.7+ features, while keeping obsolete 2.4-specific rules active.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At libs/analytics-api-client-ruby/.rubocop.yml, line 4:

<comment>RuboCop `TargetRubyVersion` is set to `2.4` but the gemspec requires `>= 2.7`. This overrides auto-detection and prevents RuboCop from applying cops relevant to 2.7+ features, while keeping obsolete 2.4-specific rules active.</comment>

<file context>
@@ -0,0 +1,148 @@
+# This file is based on https://github.com/rails/rails/blob/master/.rubocop.yml (MIT license)
+# Automatically generated by OpenAPI Generator (https://openapi-generator.tech)
+AllCops:
+  TargetRubyVersion: 2.4
+  # RuboCop has a bunch of cops enabled by default. This setting tells RuboCop
+  # to ignore them, so only the ones explicitly set in this file are enabled.
</file context>
Fix with cubic

-r{toxinidir}/test-requirements.txt

commands=
pytest --cov=daytona_analytics_api_client_async

@cubic-dev-ai cubic-dev-ai Bot Jun 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The tox test command is configured to run pytest even though this package has no tests, which makes tox fail by default with pytest exit code 5 (no tests collected).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At libs/analytics-api-client-python-async/tox.ini, line 9:

<comment>The tox test command is configured to run pytest even though this package has no tests, which makes tox fail by default with pytest exit code 5 (no tests collected).</comment>

<file context>
@@ -0,0 +1,9 @@
+     -r{toxinidir}/test-requirements.txt
+
+commands=
+   pytest --cov=daytona_analytics_api_client_async
</file context>
Fix with cubic

* @return The bearer token
*/
public String getBearerToken() {
return tokenSupplier.get();

@cubic-dev-ai cubic-dev-ai Bot Jun 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: HttpBearerAuth.getBearerToken() throws NullPointerException when called before tokenSupplier is set.

The field tokenSupplier is never initialized (defaults to null), so calling getBearerToken() before setBearerToken(...) will crash with NPE. The applyToParams method in the same class handles a null supplier safely via Optional.ofNullable, making this an inconsistency that will surprise callers using the public getter.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At libs/analytics-api-client-java/src/main/java/io/daytona/analytics/api/client/auth/HttpBearerAuth.java, line 40:

<comment>HttpBearerAuth.getBearerToken() throws NullPointerException when called before tokenSupplier is set.

The field `tokenSupplier` is never initialized (defaults to null), so calling `getBearerToken()` before `setBearerToken(...)` will crash with NPE. The `applyToParams` method in the same class handles a null supplier safely via `Optional.ofNullable`, making this an inconsistency that will surprise callers using the public getter.</comment>

<file context>
@@ -0,0 +1,75 @@
+   * @return The bearer token
+   */
+  public String getBearerToken() {
+    return tokenSupplier.get();
+  }
+
</file context>
Suggested change
return tokenSupplier.get();
public String getBearerToken() {
return Optional.ofNullable(tokenSupplier).map(Supplier::get).orElse(null);
}
Fix with cubic

*
* @param statusCode The status code of HTTP response
* @param headers The headers of HTTP response
* @param data The object deserialized from response bod

@cubic-dev-ai cubic-dev-ai Bot Jun 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Javadoc typo: "response bod" should be "response body".

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At libs/analytics-api-client-java/src/main/java/io/daytona/analytics/api/client/ApiResponse.java, line 42:

<comment>Javadoc typo: "response bod" should be "response body".</comment>

<file context>
@@ -0,0 +1,76 @@
+     *
+     * @param statusCode The status code of HTTP response
+     * @param headers The headers of HTTP response
+     * @param data The object deserialized from response bod
+     */
+    public ApiResponse(int statusCode, Map<String, List<String>> headers, T data) {
</file context>
Fix with cubic

.cache
nosetests.xml
coverage.xml
*,cover

@cubic-dev-ai cubic-dev-ai Bot Jun 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Typo in gitignore pattern: *,cover should be *.cover (dot instead of comma). The current pattern never matches any real coverage output file.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At libs/analytics-api-client-python/.gitignore, line 45:

<comment>Typo in gitignore pattern: `*,cover` should be `*.cover` (dot instead of comma). The current pattern never matches any real coverage output file.</comment>

<file context>
@@ -0,0 +1,66 @@
+.cache
+nosetests.xml
+coverage.xml
+*,cover
+.hypothesis/
+venv/
</file context>
Suggested change
*,cover
*.cover
Fix with cubic

from pydantic import BaseModel, ConfigDict, Field, StrictFloat, StrictInt, StrictStr
from typing import Any, ClassVar, Dict, List, Optional, Union
from pydantic import TypeAdapter
from typing import Optional, Set

@cubic-dev-ai cubic-dev-ai Bot Jun 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Optional is imported twice (lines 16 and 19). Merge Set into the first import and drop the second line to eliminate the flake8 F811 violation.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At libs/analytics-api-client-python/daytona_analytics_api_client/models/models_usage_period.py, line 23:

<comment>`Optional` is imported twice (lines 16 and 19). Merge `Set` into the first import and drop the second line to eliminate the flake8 F811 violation.</comment>

<file context>
@@ -0,0 +1,114 @@
+from pydantic import BaseModel, ConfigDict, Field, StrictFloat, StrictInt, StrictStr
+from typing import Any, ClassVar, Dict, List, Optional, Union
+from pydantic import TypeAdapter
+from typing import Optional, Set
+from typing_extensions import Self
+
</file context>
Fix with cubic

Comment on lines +216 to +217
case reflect.Float32, reflect.Float64:
value = strconv.FormatFloat(v.Float(), 'g', -1, 32)

@cubic-dev-ai cubic-dev-ai Bot Jun 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Float64 query parameters are formatted with 32‑bit precision, which truncates float64 values when they’re added to query strings. Split the float32 and float64 cases so float64 uses 64‑bit formatting.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At libs/analytics-api-client-go/client.go, line 216:

<comment>Float64 query parameters are formatted with 32‑bit precision, which truncates float64 values when they’re added to query strings. Split the float32 and float64 cases so float64 uses 64‑bit formatting.</comment>

<file context>
@@ -0,0 +1,656 @@
+			case reflect.Uint, reflect.Uint8, reflect.Uint16,
+				reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+				value = strconv.FormatUint(v.Uint(), 10)
+			case reflect.Float32, reflect.Float64:
+				value = strconv.FormatFloat(v.Float(), 'g', -1, 32)
+			case reflect.Bool:
</file context>
Suggested change
case reflect.Float32, reflect.Float64:
value = strconv.FormatFloat(v.Float(), 'g', -1, 32)
case reflect.Float32:
value = strconv.FormatFloat(v.Float(), 'g', -1, 32)
case reflect.Float64:
value = strconv.FormatFloat(v.Float(), 'g', -1, 64)
Fix with cubic

@rajpratham1 rajpratham1 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the significant work on this feature. The overall direction looks valuable, but I don't think this PR is in a reviewable state yet.

My main concerns are:

This PR combines multiple independent changes (daemon metrics, telemetry APIs, SDK updates, generated clients, release workflow, documentation, and dashboard changes) into a single 33k+ line change set.
Most of the added files appear to be generated SDK code, which makes it difficult to identify and review the actual implementation logic.
Mixing generated artifacts with handwritten code significantly increases review complexity and future maintenance.
Release workflow changes should ideally be reviewed independently from the feature itself.

I'd recommend splitting this into smaller PRs, for example:

Core daemon/telemetry implementation.
OpenAPI specification updates.
Generated SDK/client updates.
Dashboard/UI changes.
Documentation and release workflow updates.

Breaking the work into focused PRs will make it much easier to validate correctness, review changes thoroughly, and reduce regression risk.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants