Skip to content

The sweep

A sweep is one pass over every family whose interval has elapsed. It is an increment, not a rebuild: it asks for the little that can have changed since last time, writes what it got to every configured store, and records when each family ran.

Two things in that diagram are the whole design. Every collector produces dated points, and the stores that cannot hold a date get them reduced to current values first, by the same reducer, before they ever see the data. That is the subject of dating a point.

The surfaces move at wildly different speeds. Workflow runs finish every few minutes on a busy account. The contribution calendar changes once a day. The list of forks changes a few times a year. One interval for all of them would either waste the rate limit on the slow ones or lose the fast ones, so each family carries its own.

FamilyDefaultCollects
actions15mWorkflow runs, jobs, steps, the Actions cache
ratelimit15mWhat the collector has left to spend, in each budget
activity30mThe repository activity log, where a force push is recorded
events30mThe account event feed, which keeps only the last 300
notifs30mThe notification inbox
artifacts1hArtifacts and their expiry
commits1hLines changed and signature state, per commit
deployments1hDeployments and their environments, batched over every repository
issueevents1hThe timeline of what moved: labels, assignments, transitions
issues1hPull requests, issues and reviews, per item
repo1hStars, forks, languages, topics, releases, rulesets
security1hDependabot and code scanning alerts
discussions2hThe forum half of a repository
analyses6hCode scanning analyses, which GitHub prunes
billing6hUsage per day, product, SKU and repository
planning6hLabels and milestones
settings6hWebhooks and their deliveries, environments, deploy keys
stars6hThe stargazer walk once, then the newest hundred
traffic6hThe whole 14-day window, rewritten
account12hProfile, contribution calendar, contribution totals
forks12hWho forked, and when
outbound12hStars given, and work in other people’s repositories
profile12hPackages, gists, social accounts
stats12hCommits per week, the punch card, the workflow definitions
totals12hThe lifetime numbers, asked of GitHub rather than added up here
achievements24hThe profile badges, and the distance to each next tier
branches24hWhich branches are live and how stale each tip is
inventory24hWhat a workflow’s own token may do, both secret stores, default code scanning
keys24hThe account’s SSH and GPG keys, and when each expires
policyfiles24hSECURITY.md, CODEOWNERS, dependabot.yml and FUNDING.yml
rulesets24hEvery version of every ruleset’s changelog
depsoffThe dependency SBOM of each repository, and what changed
historyoffEach year’s contribution calendar, the current one included
joblogsoffThe tail of every failed job’s log

Setting any of them to 0 switches it off entirely. See cadences.

The repository list is rebuilt at most once an hour. Repositories are created rarely and listing them costs a page per hundred, so anything shorter spends quota to learn nothing. Forks and archived repositories are excluded by default, for the reason set out in targets.

A family that fails on one repository is logged and skipped; the sweep continues. This matters more than it sounds, because “failure” here is usually a feature being switched off: of fifty repositories, most have Dependabot off, and each of them answers 403. Treating that as an error would lose the other forty-nine.

There is one exception, and it is deliberate. A family where every repository failed is not marked as run. Marking it would hide the outage until the next cadence, which for the twelve-hour families is half a day.

level=WARN msg="family failed everywhere, not marking it as run" family=security

state_file holds six things, and the two a sweep is judged by are when each family last ran and when each repository was first seen.

The second is what makes the one-off full walk of the star history happen once instead of on every sweep. It is written through a temporary file and renamed, so a crash mid-write cannot leave a truncated state that would trigger a full re-collection.

Before each family the collector checks the three rate buckets it actually spends from. If any of them is at or below its reserve and the window has not reset yet, the family is skipped and a warning is logged rather than the budget being spent to the last call. See rate limits.

A backfill is the opposite intention: it waits for the window to turn over instead of skipping.