Skip to content

SponsorsService's paginated activeBounties/activeMilestones/milestoneProgress use take/skip with no ORDER BY, so pages overlap and skip rows #464

Description

@chonilius

Problem

#279 added limit and offset to three of SponsorsService's dashboard lists, but none of them define an order:

// activeBounties
this.bountyRepo.createQueryBuilder('bounty')
  .where('bounty.sponsorId = :sponsorId', { sponsorId })
  .andWhere('bounty.status NOT IN (:...terminal)', { ... })
  .take(options.limit ?? 20)
  .skip(options.offset ?? 0)
  .getMany();

// activeMilestones
this.milestoneRepo.find({ where: [...], take: ..., skip: ... });

// milestoneProgress
this.milestoneRepo.find({ where: { sponsorId }, take: ..., skip: ... });

Without ORDER BY, Postgres gives no guarantee about row order between two executions of the same query. The plan can change, for example between the new (sponsorId, status) index scan (#345) and a sequential scan, or after concurrent updates and VACUUM. So offset=0 and offset=20 can return the same bounty twice and never return others. A sponsor paging through their dashboard can miss active bounties entirely.

The recentPayments list in the same dashboard() already has .orderBy('payment.createdAt', 'DESC'), so only these three lists are affected.

Suggested fix

Add a deterministic order with a unique tiebreaker to each of the three queries:

  • activeBounties: .orderBy('bounty.createdAt', 'DESC').addOrderBy('bounty.id', 'DESC')
  • activeMilestones / milestoneProgress: order: { createdAt: 'DESC', id: 'DESC' }

Extend sponsors.service.spec.ts (#141) to assert the order is applied.

Related: #279, #142, #345, #346.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Stellar WaveIssues in the Stellar wave programbugSomething isn't workinggood first issueGood for newcomersperformancePerformance/optimization issue

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions