Skip to content

Commit 384004d

Browse files
aparajonampagent
andcommitted
fix(planetscale): persist recovered migration context
Amp-Thread-ID: https://ampcode.com/threads/T-019e7457-78b6-742d-8b89-33144cc92e27 Co-authored-by: Amp <amp@ampcode.com>
1 parent ca91b28 commit 384004d

2 files changed

Lines changed: 103 additions & 4 deletions

File tree

pkg/tern/local_client.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,7 @@ func (c *LocalClient) Progress(ctx context.Context, req *ternv1.ProgressRequest)
734734
// can poll the deploy request and query SHOW VITESS_MIGRATIONS.
735735
var engineResult *engine.ProgressResult
736736
var vitessApplyIsInstant bool
737+
var vitessApplyData *storage.VitessApplyData
737738
// Query engine for live progress. For Vitess, also query during pending state
738739
// to surface PlanetScale states (preparing branch, deploy request, etc.).
739740
queryDuringPending := c.config.Type == storage.DatabaseTypeVitess
@@ -756,6 +757,7 @@ func (c *LocalClient) Progress(ctx context.Context, req *ternv1.ProgressRequest)
756757
case vad == nil:
757758
c.logger.Warn("VitessApplyData not found for progress — apply may still be initializing", "apply_id", activeTask.ApplyID)
758759
default:
760+
vitessApplyData = vad
759761
vitessApplyIsInstant = vad.IsInstant
760762
resumeState, resumeErr := planetscale.BuildResumeState(planetscaleResumeData(vad))
761763
if resumeErr != nil {
@@ -768,6 +770,12 @@ func (c *LocalClient) Progress(ctx context.Context, req *ternv1.ProgressRequest)
768770
result, err := eng.Progress(ctx, progressReq)
769771
if err == nil {
770772
engineResult = result
773+
if c.config.Type == storage.DatabaseTypeVitess && vitessApplyData != nil && result.ResumeState != nil && result.ResumeState.MigrationContext != "" && vitessApplyData.MigrationContext != result.ResumeState.MigrationContext {
774+
vitessApplyData.MigrationContext = result.ResumeState.MigrationContext
775+
if saveErr := c.storage.VitessApplyData().Save(ctx, vitessApplyData); saveErr != nil {
776+
c.logger.Warn("failed to persist recovered Vitess migration context", "apply_id", activeTask.ApplyID, "migration_context", result.ResumeState.MigrationContext, "error", saveErr)
777+
}
778+
}
771779
c.logger.Info("Progress: engine returned", "engine_state", result.State, "message", result.Message, "task_id", activeTask.TaskIdentifier, "storage_state", activeTask.State)
772780
engineTaskState := taskStateFromProgressResult(result)
773781
taskState := taskStateWithNoBackwardProgress(activeTask.State, engineTaskState)

pkg/tern/local_client_test.go

Lines changed: 95 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,12 @@ func (s *exactProgressTaskStore) Update(context.Context, *storage.Task) error {
7171

7272
type fakeControlEngine struct {
7373
engine.Engine
74-
stopCount int
75-
cutoverCount int
76-
cutoverResult *engine.ControlResult
77-
cutoverErr error
74+
stopCount int
75+
cutoverCount int
76+
progressResult *engine.ProgressResult
77+
progressErr error
78+
cutoverResult *engine.ControlResult
79+
cutoverErr error
7880
}
7981

8082
func (e *fakeControlEngine) Name() string { return "fake" }
@@ -88,6 +90,9 @@ func (e *fakeControlEngine) Apply(context.Context, *engine.ApplyRequest) (*engin
8890
}
8991

9092
func (e *fakeControlEngine) Progress(context.Context, *engine.ProgressRequest) (*engine.ProgressResult, error) {
93+
if e.progressResult != nil || e.progressErr != nil {
94+
return e.progressResult, e.progressErr
95+
}
9196
return &engine.ProgressResult{State: engine.StateRunning}, nil
9297
}
9398

@@ -126,6 +131,7 @@ type exactProgressStorage struct {
126131
tasks storage.TaskStore
127132
logs storage.ApplyLogStore
128133
controlRequests storage.ControlRequestStore
134+
vitessApplyData storage.VitessApplyDataStore
129135
}
130136

131137
func (s *exactProgressStorage) Applies() storage.ApplyStore { return s.applies }
@@ -139,6 +145,30 @@ func (s *exactProgressStorage) ApplyLogs() storage.ApplyLogStore {
139145
func (s *exactProgressStorage) ControlRequests() storage.ControlRequestStore {
140146
return s.controlRequests
141147
}
148+
func (s *exactProgressStorage) VitessApplyData() storage.VitessApplyDataStore {
149+
return s.vitessApplyData
150+
}
151+
152+
type exactProgressVitessApplyDataStore struct {
153+
storage.VitessApplyDataStore
154+
data *storage.VitessApplyData
155+
saveData *storage.VitessApplyData
156+
err error
157+
}
158+
159+
func (s *exactProgressVitessApplyDataStore) GetByApplyID(context.Context, int64) (*storage.VitessApplyData, error) {
160+
return s.data, s.err
161+
}
162+
163+
func (s *exactProgressVitessApplyDataStore) Save(_ context.Context, data *storage.VitessApplyData) error {
164+
if s.err != nil {
165+
return s.err
166+
}
167+
clone := *data
168+
s.saveData = &clone
169+
s.data = data
170+
return nil
171+
}
142172

143173
func TestApplyCancelHandleDoesNotCancelNewerOwner(t *testing.T) {
144174
client := &LocalClient{}
@@ -291,6 +321,67 @@ func TestLocalClient_ProgressByApplyIDReturnsNotFoundForMissingApplyData(t *test
291321
}
292322
}
293323

324+
func TestLocalClient_ProgressPersistsRecoveredVitessMigrationContext(t *testing.T) {
325+
apply := &storage.Apply{
326+
ID: 42,
327+
ApplyIdentifier: "apply-vitess-recovered-context",
328+
Database: "testdb",
329+
DatabaseType: storage.DatabaseTypeVitess,
330+
Environment: "staging",
331+
State: state.Apply.Running,
332+
Engine: storage.EnginePlanetScale,
333+
}
334+
task := &storage.Task{
335+
ID: 43,
336+
ApplyID: apply.ID,
337+
TaskIdentifier: "task-vitess-recovered-context",
338+
Database: "testdb",
339+
DatabaseType: storage.DatabaseTypeVitess,
340+
Namespace: "testdb",
341+
TableName: "users",
342+
State: state.Task.Running,
343+
}
344+
vitessData := &storage.VitessApplyData{
345+
ApplyID: apply.ID,
346+
BranchName: "schemabot-apply",
347+
DeployRequestID: 123,
348+
ExistingMigrationCtxs: map[string]storage.VitessMigrationContextTimestamps{
349+
"preexisting": {RequestedTimestamp: "2026-01-01 00:00:00"},
350+
},
351+
}
352+
vitessStore := &exactProgressVitessApplyDataStore{data: vitessData}
353+
client := &LocalClient{
354+
config: LocalConfig{
355+
Database: "testdb",
356+
Type: storage.DatabaseTypeVitess,
357+
},
358+
storage: &exactProgressStorage{
359+
applies: &exactProgressApplyStore{apply: apply},
360+
tasks: &exactProgressTaskStore{tasks: []*storage.Task{task}},
361+
vitessApplyData: vitessStore,
362+
},
363+
planetscaleEngine: &fakeControlEngine{progressResult: &engine.ProgressResult{
364+
State: engine.StateRunning,
365+
ResumeState: &engine.ResumeState{
366+
MigrationContext: "recovered-context",
367+
},
368+
}},
369+
logger: slog.Default(),
370+
}
371+
372+
_, err := client.Progress(t.Context(), &ternv1.ProgressRequest{
373+
ApplyId: apply.ApplyIdentifier,
374+
Environment: apply.Environment,
375+
})
376+
require.NoError(t, err)
377+
require.NotNil(t, vitessStore.saveData)
378+
assert.Equal(t, "recovered-context", vitessStore.saveData.MigrationContext)
379+
assert.Equal(t, apply.ID, vitessStore.saveData.ApplyID)
380+
assert.Equal(t, "schemabot-apply", vitessStore.saveData.BranchName)
381+
assert.Equal(t, uint64(123), vitessStore.saveData.DeployRequestID)
382+
assert.Contains(t, vitessStore.saveData.ExistingMigrationCtxs, "preexisting")
383+
}
384+
294385
func TestLocalClient_ProcessPendingStopControlRequest(t *testing.T) {
295386
apply := &storage.Apply{
296387
ID: 123,

0 commit comments

Comments
 (0)