Skip to content

Add LocalScale deploy request state machine#6

Merged
aparajon merged 1 commit intomainfrom
localscale-deploy-requests-2026-04-09
Apr 10, 2026
Merged

Add LocalScale deploy request state machine#6
aparajon merged 1 commit intomainfrom
localscale-deploy-requests-2026-04-09

Conversation

@aparajon
Copy link
Copy Markdown
Collaborator

@aparajon aparajon commented Apr 9, 2026

Summary

Adds a deploy request lifecycle to LocalScale's fake PlanetScale API, enabling end-to-end testing of the PlanetScale engine's branch→deploy→cutover→revert flow without a real PlanetScale account.

  • A background state processor polls Vitess migration statuses every 500ms and drives deploy requests through their full lifecycle: pending → ready → submitting → queued → in_progress → pending_cutover → complete_pending_revert → complete.
  • Supports cancel, revert, skip-revert, throttle control (capped at 0.95 per PlanetScale behavior), and revert window auto-expiry
  • New deploys are blocked while a previous deploy's revert window is open, matching PlanetScale's gated deployment behavior.

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings April 9, 2026 23:01
@aparajon aparajon force-pushed the localscale-deploy-requests-2026-04-09 branch 2 times, most recently from a8f8d99 to 70755ac Compare April 9, 2026 23:09
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a full deploy-request lifecycle to LocalScale’s fake PlanetScale API so the engine can be tested end-to-end (branch → deploy → cutover → revert/skip) without a real PlanetScale account.

Changes:

  • Introduces localscale_deploy_requests metadata table and HTTP endpoints for deploy-request CRUD + actions (deploy, cancel, cutover, revert, skip-revert, throttle).
  • Adds a background processor that polls Vitess migration status and advances deploy-request states automatically.
  • Extends LocalScale configuration/CLI config to support revert-window timing, default throttling, and optional artificial delays.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
pkg/localscale/server.go Wires new deploy-request routes, starts/stops background processor, adds config fields and reset behavior.
pkg/localscale/schema/localscale_deploy_requests.sql New metadata table backing the deploy-request state machine.
pkg/localscale/processor.go Background state machine that derives deploy-request state from Vitess migration status.
pkg/localscale/helpers.go Adds helpers for DDL strategy, schema snapshot/diff for revert, deploy request utilities, and branch DB cleanup.
pkg/localscale/handlers_deploy.go Implements deploy-request create/get and deploy initiation flows.
pkg/localscale/handlers_actions.go Implements deploy-request action endpoints (cancel/cutover/revert/skip/throttle) and Vitess migration helpers.
pkg/localscale/handlers_branches.go Updates branch creation snapshot flow and adds optional artificial delay.
cmd/localscale/main.go Adds CLI config parsing for new durations and throttle configuration.
Comments suppressed due to low confidence (1)

pkg/localscale/handlers_branches.go:165

  • Branch databases are now created on backend.mysqlDSNBase, but most branch operations (e.g., openBranchDB used by GET branch schema / apply branch schema / admin branch-db-query) still connect via s.branchDSNBase (derived from the first managed cluster). With multiple org/database backends this will create branch DBs on one mysqld and try to read/write them on another. Consider either keeping branch DBs on the shared metadata mysqld, or refactoring openBranchDB and all branch DB operations to use the per-backend mysqlDSNBase.
		// Open a connection to the backend's mysqld for branch database creation.
		// Each org/database has its own managed cluster with its own mysqld.
		s.logger.Info("branch snapshot: opening backend mysqld", "dsn_prefix", backend.mysqlDSNBase[:min(len(backend.mysqlDSNBase), 40)])
		backendDB, err := sql.Open("mysql", backend.mysqlDSNBase)
		if err != nil {
			s.logger.Error("open backend mysqld for branch creation", "error", err)
			snapshotFailed = true
			snapshotErrors = append(snapshotErrors, fmt.Sprintf("open backend mysqld: %v", err))
		}
		if backendDB != nil {
			if err := backendDB.PingContext(bgCtx); err != nil {
				s.logger.Error("ping backend mysqld for branch creation", "error", err)
				utils.CloseAndLog(backendDB)
				backendDB = nil
				snapshotFailed = true
				snapshotErrors = append(snapshotErrors, fmt.Sprintf("ping backend mysqld: %v", err))
			} else {
				defer utils.CloseAndLog(backendDB)
			}
		}

		// Snapshot schema from vtgate into branch databases for each keyspace.
		s.logger.Info("branch snapshot: iterating keyspaces", "count", len(backend.vtgateDBs), "branch", body.Name)
		for keyspace := range backend.vtgateDBs {
			s.logger.Info("branch snapshot: processing keyspace", "keyspace", keyspace, "branch", body.Name)
			dbName := branchDBName(body.Name, keyspace)

			if backendDB == nil {
				snapshotFailed = true
				continue
			}

			// Create branch database on the backend's mysqld
			if err := validateIdentifier(dbName); err != nil {
				s.logger.Error("invalid branch database name", "name", dbName, "error", err)
				snapshotFailed = true
				snapshotErrors = append(snapshotErrors, fmt.Sprintf("invalid branch database name %s: %v", dbName, err))
				continue
			}
			if _, err := backendDB.ExecContext(bgCtx, "CREATE DATABASE IF NOT EXISTS "+quoteIdentifier(dbName)); err != nil {
				s.logger.Error("create branch database", "branch", body.Name, "keyspace", keyspace, "error", err)
				snapshotFailed = true
				snapshotErrors = append(snapshotErrors, fmt.Sprintf("create branch database %s: %v", keyspace, err))
				continue
			}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@aparajon aparajon force-pushed the localscale-deploy-requests-2026-04-09 branch 26 times, most recently from c993bd6 to deafec5 Compare April 10, 2026 08:05
@aparajon aparajon force-pushed the localscale-deploy-requests-2026-04-09 branch 6 times, most recently from a3d2906 to 31ad58f Compare April 10, 2026 15:52
Add a deploy request lifecycle to LocalScale's fake PlanetScale API, enabling
end-to-end testing of the PlanetScale engine's branch→deploy→cutover→revert flow
without a real PlanetScale account.

The state machine processor runs as a background goroutine, polling Vitess
migration statuses every 500ms and driving deploy requests through their
lifecycle: pending → ready → submitting → queued → in_progress → pending_cutover
→ complete_pending_revert → complete. Supports cancel, revert (via reverse DDL
computation), skip-revert, throttle control, and auto-cutover.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@aparajon aparajon force-pushed the localscale-deploy-requests-2026-04-09 branch from 31ad58f to 4944f91 Compare April 10, 2026 16:17
@aparajon aparajon marked this pull request as ready for review April 10, 2026 17:07
@aparajon aparajon requested a review from morgo as a code owner April 10, 2026 17:07
@aparajon aparajon merged commit ee1cc50 into main Apr 10, 2026
14 checks passed
@aparajon aparajon deleted the localscale-deploy-requests-2026-04-09 branch April 10, 2026 17:32
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.

3 participants