Skip to content
Snippets Groups Projects

campaigns: Cache external changeset diffstats

Merged Administrator requested to merge aharvey/cache-external-changeset-diffs into master

Created by: LawnGnome

Fixes #11075.

The general approach here is to cache the changeset diffstat if the base or head OID has changed when we sync the changeset, then use that cached changeset when a GraphQL query is made that requires a campaign or changeset diffstat.

There is one observable behaviour change: closed changesets previously returned zero diffstats, but now return their actual diffstat. I think this is OK, and it simplifies the implementation, but we could force the syncer to cache zeroes for closed changesets to return to the old behaviour. I've kept the relevant test change in a separate commit for now to make it easier to change this if necessary.

Since campaign diffstats require knowledge of which changesets are accessible to the user, I've elected to use the simple approach for now of continuing to use the GraphQL connection. (See the discussion below between @mrnugget and I for more detail.) This is technically suboptimal, as we're pulling in sync data that we don't need to, but I think that's likely OK pending further work on being able to resolve permissions at the store level. I'm also happy to attempt @mrnugget's suggested optimisation in a future PR.

Merge request reports

Merged by avatar (Jul 8, 2025 6:36am UTC)

Loading

Activity

Filter activity
  • Approvals
  • Assignees & reviewers
  • Comments (from bots)
  • Comments (from users)
  • Commits & branches
  • Edits
  • Labels
  • Lock status
  • Mentions
  • Merge request status
  • Tracking
  • Created by: codecov[bot]

    Codecov Report

    Merging #11279 into master will increase coverage by 0.35%. The diff coverage is 40.41%.

    @@            Coverage Diff             @@
    ##           master   #11279      +/-   ##
    ==========================================
    + Coverage   47.13%   47.49%   +0.35%     
    ==========================================
      Files        1402     1401       -1     
      Lines       79484    79583      +99     
      Branches     6775     6665     -110     
    ==========================================
    + Hits        37467    37798     +331     
    + Misses      38443    38217     -226     
    + Partials     3574     3568       -6     
    Flag Coverage Δ
    #go 51.66% <40.00%> (-0.07%) :arrow_down:
    #storybook 10.12% <ø> (?)
    #typescript 36.42% <100.00%> (+1.45%) :arrow_up:
    #unit 47.09% <40.41%> (-0.05%) :arrow_down:
    Impacted Files Coverage Δ
    cmd/frontend/graphqlbackend/campaigns.go 0.00% <ø> (ø)
    web/src/enterprise/campaigns/detail/backend.ts 10.71% <ø> (ø)
    enterprise/internal/campaigns/state.go 61.16% <12.00%> (-17.18%) :arrow_down:
    internal/campaigns/types.go 11.49% <42.85%> (+0.92%) :arrow_up:
    ...terprise/internal/campaigns/resolvers/campaigns.go 63.13% <75.00%> (-6.07%) :arrow_down:
    ...erprise/internal/campaigns/resolvers/changesets.go 64.77% <75.00%> (-0.51%) :arrow_down:
    enterprise/internal/campaigns/store.go 85.52% <80.00%> (-0.11%) :arrow_down:
    enterprise/internal/campaigns/syncer.go 78.62% <85.71%> (+0.19%) :arrow_up:
    enterprise/internal/campaigns/webhooks.go 37.10% <100.00%> (ø)
    enterprise/internal/campaigns/workers.go 47.28% <100.00%> (ø)
    ... and 31 more
  • Created by: LawnGnome

    I ended up making a couple more cleanups along the way, but this is now ready for review.

  • Created by: LawnGnome

    One "Hmm"-thought I had that I wanted to mention (and I'm curious to hear your thoughts): I'm not 100% sure about the name ChangesetSyncState and sync_state. I think I get what it's used for, something like an internal cache, but at the same time I'm wondering: "why isn't ExternalState and ExternalReviewState etc. in sync_data?" I'm curious to hear how you think about that column and what should go in and what not.

    To me, this column is for state that needs to be persisted between syncs that is internal to enterprise/internal/campaigns: fields that we're not surfacing via GraphQL, but rather fields that we need to compute the fields that are surfaced.

    I'm open to better naming, of course.

    Technically, we could move the external fields into this JSON blob, but I think having that separation between "here are things we know we need, and we're calling them out in the schema as explicit fields" and "here is a blob of state that is hidden behind the curtain" helps with understanding the data model.

  • Created by: mrnugget

    To me, this column is for state that needs to be persisted between syncs that is internal to enterprise/internal/campaigns:

    That makes sense to me, thanks! (Would also be a great doc comment, in case that's not there yet)

  • Created by: LawnGnome

    Would also be a great doc comment, in case that's not there yet

    I added a more concise version of that explanation to the SyncState field.

  • Created by: mrnugget

    Feel free to merge, btw.

Please register or sign in to reply
Loading