Geo: Replicate Projects::Topic uploads (project_topic_uploads)

What does this MR do and why?

Adds Geo SSF (self-service framework) replication and verification for the project_topic_uploads partition table so Projects::Topic attachments (topic avatars) are mirrored from the primary to secondary Geo sites alongside the other upload partitions.

Generated with scripts/geo/generate-blob-replicator:

scripts/geo/generate-blob-replicator \
  --replicable-name=project_topic_upload \
  --model-class='Projects::Topic' \
  --table-name=project_topic_uploads \
  --sharding-key=organization_id \
  --milestone=19.1 \
  --upload-partition \
  --parent-factory=topic

Three minor cleanups were made on top of the generated output:

  1. Registry template whitespace. Removed trailing whitespace and extra blank line in Geo::ProjectTopicUploadRegistry (known template hygiene issue tracked in !234804 (merged)).
  2. Migration line breaks. Broke add_concurrent_foreign_key lines in the states migration and the registry index line to stay within the 120-character RuboCop limit.
  3. RuboCop alignment. Auto-corrected spacing in the registries shared context.

Selective sync uses the organization-only scope (parity with Geo::VulnerabilityExportUpload), since project_topic_uploads is sharded by organization_id only.

References

How to set up and validate locally

Requires a primary + secondary Geo setup (e.g. two GDKs).

  1. On both sites, run migrations:

    bin/rake db:migrate db:migrate:geo
  2. On the primary, enable the feature flag:

    Feature.enable(:geo_project_topic_upload_replication)
  3. Create a Projects::Topic with an avatar (Admin > Topics > New topic, attach an image).

  4. On the secondary, confirm the registry row was created and replication + verification succeeded:

    Geo::ProjectTopicUploadRegistry.last
    # => state: 'synced', verification_state: 'succeeded'
  5. Confirm the file is present at the secondary's upload path and that verification_checksum matches the primary's.

  6. Optionally spot-check the GraphQL API:

    {
      geoNode(name: "<secondary-name>") {
        projectTopicUploadRegistries { nodes { id state } }
      }
    }
Edited by Scott Murray

Merge request reports

Loading