Incremental checkpoint read engine + FF (phase 2, 1/3)

What does this MR do?

Part 1/3 of the phase-2 incremental checkpoint read path (split out of !242398 (closed)). Adds the read engine with no consumer yet:

  • Gitlab::DuoWorkflow::ChannelValuesReconstructor — folds incremental checkpoint blobs into channel_values (compaction ⇒ replace, conversation ⇒ append; dict-of-list channels supported). Guards corrupt/undecodable blob data.
  • Ai::DuoWorkflows::Workflow read methodscheckpoint_header_for, latest_checkpoint_header, accumulated_blobs_for, ancestor_thread_ts, reconstructed_channel_values, and the reconstruct_from_blobs? / read_incremental_checkpoints_enabled? gates. accumulated_blobs_for selects the blobs on a checkpoint's ancestor chain within one current_thread group, bounded to a single daily partition.
  • duo_workflow_read_incremental_checkpoints feature flag (wip, default off).

No API surface here — the internal by_thread_ts endpoint and the trace read path land as separate MRs stacked on this branch.

Database

All read queries are keyed by workflow_id + workflow_created_at, so each prunes to a single daily partition via index scan (no seq scans, no cross-partition scans). Plans on the GDK DB:

checkpoint_header_for / latest_checkpoint_header / ancestor_thread_ts (p_duo_workflows_checkpoint_headers):

 Sort (cost=3.20..3.21 rows=1)
   ->  Index Scan using p_duo_workflows_checkpoint_headers_20260720_workflow_id_idx
         on p_duo_workflows_checkpoint_headers_20260720
         Index Cond: (workflow_id = 123)
         Filter: (workflow_created_at = '2026-07-20 12:00:00+00' AND thread_ts = 'ts-1')

accumulated_blobs_for (p_duo_workflows_checkpoint_blobs). Filtering project_id — the leading column of idx_duo_wf_checkpoint_blobs_dedup (project_id, workflow_id, thread_ts, channel, version, step_action, workflow_created_at) — lets that existing unique index serve the lookup, so no new index is needed:

 Index Scan using idx_duo_wf_checkpoint_blobs_dedup (partition local copy)
   on p_duo_workflows_checkpoint_blobs_20260720
   Index Cond: (project_id = 55 AND workflow_id = 123
                AND thread_ts = ANY('{ts-1,ts-2}')
                AND workflow_created_at = '2026-07-20 12:00:00+00')
   Filter: (current_thread = 0)

workflow_created_at is applied by the checkpoint_blobs / checkpoint_headers association scope (partition pruning), so each read stays on one daily partition.

No new migrations — the tables, indexes, and partition scheme all come from the base branch (!244403 (merged), !244914 (merged)).

Feature flag

duo_workflow_read_incremental_checkpoints (wip, default off). Read path is a no-op until enabled and until a consumer MR merges.

Testing

Reconstructor and model specs updated and green.

Related: #604677 (closed)


Restructure — epic &22939

Part of the phase-2 read-path epic. Each consumer is gated by its own flag in addition to the master kill switch.

  • Delivery issue: #604677 (closed)
  • Feature flag: duo_workflow_read_incremental_checkpoints (master kill switch)
  • FF rollout issue: #604687

Code still gates on the shared duo_workflow_read_incremental_checkpoints flag today; repointing to the per-consumer flag is deferred until the restructure lands.

Edited by Eduardo Bonet

Merge request reports

Loading