Connect by JBRH Open Connect

Headline numbers that stop at the page size

A slice taken for a list — the newest 500 rows, the newest 400 threads — becomes a lie the moment anything counts it. Two measured cases: a workspace with 540 contacts was told it had 500 organisations when it had 530, and 0 already contacted when it had 40; a workspace with 430 conversations saw a Closed tab reading 0 and page five empty.

Status
Available What this means
Audience
both, developer
Last verified
Product version
6.3.2

The bug in one sentence#

Something fetches a bounded slice for a good reason and then derives an unbounded fact from it. The limit was correct for the list; it is wrong for every number computed over the list, and nothing in the code says which of the two a given expression is doing.

The tell is a total that stops moving at a round number. A workspace that grows and grows and reports exactly 500 of something is not reporting a coincidence; it is reporting the size of the slice.

Two measured cases#

SurfaceWhat it readReportedActual
Prospect summaryThe newest 500 discovered organisations500 organisations530
Prospect summaryThe newest 500 contacts, whose already-contacted rows were the oldest0 already contacted40
Conversation list400 threads, then counted, filtered, searched and paged in Python over that sliceClosed tab: 0; total: 400; page five: empty430 conversations, 25 of them closed

The second row is the one worth staring at. Not "slightly low" — zero. The already-contacted contacts were the oldest, so every one of them fell outside a slice taken newest-first. A category that is entirely settled is exactly the category most likely to be old, and therefore exactly the one this defect erases completely rather than partially.

The same asymmetry produced the empty Closed tab: 25 closed conversations, all older than the 400 most recent, counted over a window that contained none of them.

Why it survives review and testing#

  • It is correct below the limit. Every number is right for every workspace smaller than the slice, which is most of them early on, and all of them in a fresh test database.
  • The fixture is smaller than the limit. A suite that creates twelve contacts can never distinguish a count from a capped count. This is the single most common reason the class ships.
  • The error is quiet and plausible. 500 is not obviously wrong; 0 already contacted looks like a workspace that has not started yet.
  • Paging and counting diverge silently. A total taken from the slice and a page taken from the database disagree only past the boundary, so page five comes back empty while the total insists there are rows.

Finding the rest of them#

  1. Grep for the limits themselves — the literal slice sizes — and read what happens to the result. Anything that counts, sums, groups or filters the fetched rows in application code is a candidate.

    Result The distinction to look for is whether the slice is being rendered or being reasoned about. Rendering is fine; reasoning is the defect.

  2. Drive it with more rows than the limit — and make the interesting rows the oldest.

    Result A fixture of 540 contacts whose 40 already-contacted ones are the oldest reproduces the exact production symptom, including the zero. An evenly distributed fixture reproduces a mild undercount and hides the severity.

  3. Ask for a page beyond the slice and check the filtered tabs.

    Result An empty page five against a non-zero total, or a tab reading 0 against rows that exist, are both this defect from a different angle.

  4. Move the counting into the database and keep the limit for the page.

    Result Counts become one GROUP BY; the filter and the page become SQL. Contacts are counted in batches with each batch's research fetched in one query; organisations are counted by the database.

Questions#

Why did a category of 40 read as 0 rather than as a smaller number?

Because the slice was newest-first and those 40 were the oldest rows. A capped count is not uniformly wrong: it is wrong in whichever direction the ordering excludes, which is why settled categories — already contacted, closed, archived — are the ones that vanish entirely.

Is a limit ever the right thing?

For a page, always. The mistake is not the limit; it is deriving a total, a tab count, a percentage or a filter result from the limited rows. Fetch the page for display and ask the database for the numbers.

How do you keep it from happening again?

By making the fixture bigger than every limit in the code path, and by putting the interesting rows outside the newest window. A suite whose data never exceeds a slice can pass forever against this class of bug.