Skip to content

feat(orchestrator): track and report last status change timestamp#2980

Merged
jakubno merged 10 commits into
mainfrom
feat/orchestrator-status-changed-at
Jun 22, 2026
Merged

feat(orchestrator): track and report last status change timestamp#2980
jakubno merged 10 commits into
mainfrom
feat/orchestrator-status-changed-at

Conversation

@jakubno

@jakubno jakubno commented Jun 11, 2026

Copy link
Copy Markdown
Member

Add service_status_changed_at to ServiceInfoResponse so the API can see when the orchestrator's status last changed. The timestamp is updated on actual status transitions and initialized to the service startup time.

@cla-bot cla-bot Bot added the cla-signed label Jun 11, 2026
@cursor

cursor Bot commented Jun 11, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
statusChangedAt is required on node API responses but can stay zero when gRPC omits or sends an invalid timestamp, and StatusInfo() can report a derived status (e.g. connecting) while the timestamp still reflects the last persisted orchestrator status.

Overview
Orchestrator service info now records when status last changed (initialized at startup, updated on transitions) and exposes it on gRPC and on admin Node / NodeDetail as statusChangedAt. The API node manager carries that timestamp through sync and cluster instance info; locally inferred unhealthy after failed sync keeps the first unhealthy time instead of refreshing it on every retry.

Reviewed by Cursor Bugbot for commit 2630a81. Bugbot is set up for automated code reviews on this repo. Configure here.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

Retrieving the node or service status and its corresponding status change timestamp via separate, non-atomic method calls introduces a race condition where the status can transition between the calls, resulting in mismatched status and timestamp values in the API responses. To ensure consistency, atomic getters should be implemented and used to retrieve both fields under a single read lock.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread packages/orchestrator/pkg/service/info.go Outdated
Comment thread packages/orchestrator/pkg/service/service_info.go Outdated
Comment thread packages/api/internal/orchestrator/nodemanager/status.go Outdated
Comment thread packages/api/internal/orchestrator/admin.go Outdated
Comment thread packages/api/internal/orchestrator/admin.go Outdated
Comment thread packages/api/internal/orchestrator/admin.go Outdated
Comment thread packages/api/internal/clusters/instance.go
@codecov

codecov Bot commented Jun 11, 2026

Copy link
Copy Markdown

❌ 8 Tests Failed:

Tests completed Failed Passed Skipped
2967 8 2959 8
View the top 3 failed test(s) by shortest run time
github.com/e2b-dev/infra/tests/integration/internal/tests/api/sandboxes::TestSnapshotTemplateDelete
Stack Traces | 0s run time
=== RUN   TestSnapshotTemplateDelete
=== PAUSE TestSnapshotTemplateDelete
=== CONT  TestSnapshotTemplateDelete
--- FAIL: TestSnapshotTemplateDelete (0.00s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
Stack Traces | 0.01s run time
=== RUN   TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
=== PAUSE TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
=== CONT  TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
    filesystem_test.go:96: 
        	Error Trace:	.../tests/envd/filesystem_test.go:96
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
--- FAIL: TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory) (0.01s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_3_lists_all_directories_and_files
Stack Traces | 0.01s run time
=== RUN   TestListDir/depth_3_lists_all_directories_and_files
=== PAUSE TestListDir/depth_3_lists_all_directories_and_files
=== CONT  TestListDir/depth_3_lists_all_directories_and_files
    filesystem_test.go:96: 
        	Error Trace:	.../tests/envd/filesystem_test.go:96
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_3_lists_all_directories_and_files
--- FAIL: TestListDir/depth_3_lists_all_directories_and_files (0.01s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_0_lists_only_root_directory
Stack Traces | 0.02s run time
=== RUN   TestListDir/depth_0_lists_only_root_directory
=== PAUSE TestListDir/depth_0_lists_only_root_directory
=== CONT  TestListDir/depth_0_lists_only_root_directory
    filesystem_test.go:96: 
        	Error Trace:	.../tests/envd/filesystem_test.go:96
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_0_lists_only_root_directory
--- FAIL: TestListDir/depth_0_lists_only_root_directory (0.02s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_1_lists_root_directory
Stack Traces | 0.02s run time
=== RUN   TestListDir/depth_1_lists_root_directory
=== PAUSE TestListDir/depth_1_lists_root_directory
=== CONT  TestListDir/depth_1_lists_root_directory
    filesystem_test.go:96: 
        	Error Trace:	.../tests/envd/filesystem_test.go:96
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_1_lists_root_directory
--- FAIL: TestListDir/depth_1_lists_root_directory (0.02s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir
Stack Traces | 0.96s run time
=== RUN   TestListDir
=== PAUSE TestListDir
=== CONT  TestListDir
--- FAIL: TestListDir (0.96s)
github.com/e2b-dev/infra/tests/integration/internal/tests/api/metrics::TestSandboxMetrics
Stack Traces | 15.7s run time
=== RUN   TestSandboxMetrics
=== PAUSE TestSandboxMetrics
=== CONT  TestSandboxMetrics
    sandbox_metrics_test.go:26: 
        	Error Trace:	.../api/metrics/sandbox_metrics_test.go:26
        	Error:      	Condition never satisfied
        	Test:       	TestSandboxMetrics
        	Messages:   	sandbox metrics not available in time
--- FAIL: TestSandboxMetrics (15.65s)
github.com/e2b-dev/infra/tests/integration/internal/tests/api/sandboxes::TestSnapshotTemplateDelete/delete_named_snapshot_by_name
Stack Traces | 16.1s run time
=== RUN   TestSnapshotTemplateDelete/delete_named_snapshot_by_name
=== PAUSE TestSnapshotTemplateDelete/delete_named_snapshot_by_name
=== CONT  TestSnapshotTemplateDelete/delete_named_snapshot_by_name
    snapshot_template_test.go:189: 
        	Error Trace:	.../api/sandboxes/snapshot_template_test.go:37
        	            				.../api/sandboxes/snapshot_template_test.go:189
        	Error:      	Not equal: 
        	            	expected: 201
        	            	actual  : 500
        	Test:       	TestSnapshotTemplateDelete/delete_named_snapshot_by_name
--- FAIL: TestSnapshotTemplateDelete/delete_named_snapshot_by_name (16.12s)

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@jakubno jakubno marked this pull request as ready for review June 11, 2026 10:00

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1312408d6e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/api/internal/orchestrator/admin.go Outdated

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1ef7bcb57c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/api/internal/orchestrator/nodemanager/status.go Outdated
Comment thread packages/api/internal/orchestrator/nodemanager/node.go Outdated
Comment thread packages/api/internal/orchestrator/nodemanager/status.go Outdated

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5b638e5acc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/api/internal/orchestrator/nodemanager/sync.go
@jakubno jakubno force-pushed the feat/orchestrator-status-changed-at branch from 5b638e5 to 8f8a752 Compare June 12, 2026 11:55

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8f8a752. Configure here.

Comment thread packages/api/internal/orchestrator/nodemanager/status.go
Comment thread packages/api/internal/orchestrator/nodemanager/sync.go

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8f8a75297b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/api/internal/orchestrator/nodemanager/sync.go Outdated
jakubno and others added 10 commits June 22, 2026 07:35
Add service_status_changed_at to ServiceInfoResponse so the API can see
when the orchestrator's status last changed. The timestamp is updated on
actual status transitions and initialized to the service startup time.
Consume service_status_changed_at from the orchestrator and track it on
nodes and cluster instances. Expose it as statusChangedAt on Node and
NodeDetail in the admin API.
- Return status and changed-at atomically under a single lock in both
  the orchestrator ServiceInfo and the API node manager to avoid
  mismatched pairs.
- Track transitions of the connectivity-derived (reported) node status
  so statusChangedAt stays consistent with the status returned to
  callers.
- Fall back to time.Now() in cluster instance sync when the status
  changes but the orchestrator does not report the timestamp.
Introduce ServiceStatus (orchestrator) and StatusInfo (api nodemanager)
so status and its change timestamp are stored and returned as a single
value instead of separate getters. Drop the instance sync time.Now()
fallback since orchestrators report the timestamp.
Drop the time.Now() fallbacks that fabricated a change timestamp when
the orchestrator does not report one. Keep time.Now() only for locally
observed changes (sync failure, connectivity-derived transitions).
Store node status as a single StatusInfo field and drop the reported
status transition tracking. The connectivity-derived status overlay
only affects the returned value; the timestamp always refers to the
last stored status change.
Drop the newer-timestamp merge logic in setStatus and store the
orchestrator-reported value unconditionally; the orchestrator is the
source of truth for when its status changed.
@jakubno jakubno force-pushed the feat/orchestrator-status-changed-at branch from 5bd7c94 to 2630a81 Compare June 22, 2026 07:38
@jakubno jakubno enabled auto-merge (squash) June 22, 2026 07:38
@jakubno jakubno merged commit f79be77 into main Jun 22, 2026
57 checks passed
@jakubno jakubno deleted the feat/orchestrator-status-changed-at branch June 22, 2026 08:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants