Skip to content

Move environment manager state to a database and queue work for data planes - #4902

Open
bhagyasakalanka wants to merge 1 commit into
thunder-id:feature/cp-dpfrom
bhagyasakalanka:envmgr-database
Open

Move environment manager state to a database and queue work for data planes#4902
bhagyasakalanka wants to merge 1 commit into
thunder-id:feature/cp-dpfrom
bhagyasakalanka:envmgr-database

Conversation

@bhagyasakalanka

@bhagyasakalanka bhagyasakalanka commented Aug 12, 2026

Copy link
Copy Markdown

What

All of the Control Plane work, as one commit.

Environment manager state moves to environmentdb

Environments and their captured versions were JSON files under environment_manager.data_dir, so each Control Plane replica kept its own copy: capture on one pod, promote on another, and the second compares against a history it never saw. They are rows now, which removes the PersistentVolumeClaim. ENVIRONMENT_VARIABLE moves across from configdb.

DEPLOYMENT_ID is the organization, not one of its environments: promotion compares one environment against another, so an organization's whole chain stays in one partition.

Work for a data plane is queued rather than requiring a live connection

A pod can only speak to the data planes that dialed it, so an apply or a credential arriving at a pod with no connection failed. Every such request is written to DATA_PLANE_JOB first and delivered by whichever pod holds the connection; the caller gets an id and reads the answer from any pod.

The pod that accepts the request delivers it immediately when it holds the connection, so the common case answers in the same response and nothing feels asynchronous. Ordering is preserved (oldest pending per data plane, one in flight). The claim is UPDATE … WHERE STATUS='pending', so racing pods settle without SKIP LOCKED and it still works on SQLite. A queued credential is encrypted with the server's configuration key.

Apply is the only funnel to a data plane, so promote, revert and apply-all inherit this. Marking an environment applied moved to delivery time: it can no longer claim to hold a version the data plane has not taken.

Reading which credentials a data plane holds no longer needs a connection either. The names it reports are recorded on the environment and answer that read, which also covers the check an apply makes before it runs.

Console

Derives the environment manager URL from the server URL and gates promotion on plane === 'cp', replacing env_manager.public_url. Shows an apply that is queued, with a control to check again, rather than reporting success before the data plane has taken it. No longer asks a generic OIDC issuer for a ThunderID user profile: /users/me is a ThunderID endpoint, and whoever signs in exists at the issuer rather than in this deployment.

Provisioning

Schema loading alone. The system tenant owns no resources, nothing seeds from it, and tenants created through /system/tenants provision themselves from the image's bundle, so the scripts that seeded it are gone.

Testing

Verified against two Control Plane pods behind an nginx load balancer, a Data Plane behind another, and PostgreSQL:

  • The load balancer put each data plane replica on a different pod, confirming the split is real.
  • With one pod holding the only connection, a queued job was claimed and delivered by that pod (claimed_by matched its hostname) while the other left it alone, and the payload arrived on the data plane.
  • A deliberately invalid import was recorded failed with the data plane's own error rather than lost or retried forever.
  • Tenant creation, environment registration and the channel handshake all exercised end to end.
  • make test_unit: 0 failures. Console tsc -b and tests clean. No lint issues in any new file.
  • Both schema scripts execute on PostgreSQL and SQLite.

Not covered

The console's queued-apply notice is built and typechecked but has not been driven through a browser session.

Note

internal/system/i18n/core/defaults.go is regenerated here. It was already stale on feature/cp-dp (secretservice renamed to secretstore without regenerating), which made make lint fail before reaching the linter. Unrelated, swept up to unblock the gate.

Checklist

  • Tested locally
  • No new dependencies
  • No CI changes

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (1)
  • main

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: bea26417-fe71-476c-bad8-4a6638c7f4b4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@bhagyasakalanka
bhagyasakalanka force-pushed the envmgr-database branch 4 times, most recently from 3230f1b to cb907ee Compare August 14, 2026 16:25
@bhagyasakalanka bhagyasakalanka changed the title Move environment manager state to its own database Move environment manager state to a database and queue work for data planes Aug 14, 2026
…planes

Environments and their captured versions moved from files under a data
directory to an environmentdb datasource, so every Control Plane replica reads
and writes the same state and no plane needs durable storage.
ENVIRONMENT_VARIABLE moves there from configdb.

A Control Plane pod can only speak to the data planes that dialed it, so an
apply or a credential arriving at a pod holding no connection failed. Every
such request is now written to that database first and delivered by whichever
pod holds the connection, with the caller given an id to read the answer back
from any pod. The pod that accepts the request delivers it immediately when it
holds the connection, so the common case answers in the same response. A
credential waiting to be delivered is encrypted with the server's
configuration key. Reading which credentials a data plane holds no longer
needs a connection either: the names it reports are recorded and answer that
read, which also covers the check an apply makes before it runs.

The console derives the environment manager URL from the server URL and gates
promotion on the Control Plane, shows an apply that is queued rather than
reporting success before the data plane has taken it, and no longer asks a
generic OIDC issuer for a ThunderID user profile.

Provisioning is now schema loading alone: the system tenant owns no resources,
so the scripts that seeded it are gone.
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.

1 participant