Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 67 additions & 11 deletions API.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 11 additions & 9 deletions docs/application-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,18 @@ ConstructHub provides two package source implementations: `NpmJs` and
`CodeArtifact`.

* The `NpmJs` source interfaces with the `npmjs.com` CouchDB replica (which is
at `replicate.npmjs.com/registry`) by scanning it's `_changes` stream in
search of relevant packages. When such a package is identified, a stager
function is invoked, which stages the package tarball into an S3 bucket then
notifies the ConstructHub ingestion SQS queue. The CouchDB follower is
scheduled to run every `5 minutes`, and stores its state (receipts for the
change entries received, and time/sequence checkpoints) in a specific object
in the S3 bucket used for staging package tarballs.
at `replicate.npmjs.com/registry`) by scanning it's `_changes` stream.
The CouchDB follower is scheduled to run every `5 minutes`. It sends every
new change entry to a queue, and stores its state (receipts for the change
entries received, and time/sequence checkpoints) in a specific object in the
S3 bucket used for staging package tarballs.
A packument processor function works through the queue. It fetches each
package from `registry.npmjs.org`, and for every relevant package version, a
stager function is invoked, which stages the package tarball into an S3
bucket then notifies the ConstructHub ingestion SQS queue.
When the registry serves an older revision of a package than the feed
announced, the package is sent to a queue, and a packument processor function
checks it again, with growing waits, for about a day.
announced, or no packument at all yet, the processor checks it again, with
growing waits. After a day it gives up on the package.
The package versions it has processed are recorded in a DynamoDB table, so
each version is only processed once.
Existing deployments kept this list in the S3 bucket; it is copied to the
Expand Down
10 changes: 6 additions & 4 deletions docs/npm-sync-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,18 +114,19 @@ The pre-migration feed also once froze its `update_seq` entirely for hours ([stu

## How ConstructHub compensates

The *NpmJs Follower* runs on a regular schedule and fetches packuments from the registry to identify new versions.
It does not track a single high-water mark:
The *NpmJs Follower* runs on a regular schedule and reads the changes feed.
It sends every change entry to a queue, where the *Packument Processor* fetches the packument from the registry to identify new versions.
The follower does not track a single high-water mark:
it keeps a receipt for every change entry it has received,
and every run re-reads a trailing window of the feed,
processing any entry it has no receipt for.
sending any entry it has no receipt for.
Rolling checkpoints map times to feed positions, and derive the window's starting position.
On top of that baseline, each gap has a dedicated mitigation:

| Gap | Mitigation | Detection |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Late insertion | Every scan re-covers the trailing window; a periodic deep scan covers the full state retention | `LateChangeCount` / `LateChangeLag` metrics; `LateChangeLagHigh` alarm |
| Laggy packuments | Process every packument the registry serves immediately; queue a re-check of the announced revision (also when the registry has no packument yet), retried with back-off for about 29 hours, then given up on | `LaggyPackuments` / `LaggyPackumentsRecovered` / `LaggyPackumentGiveUps` / `LaggyPackumentLag` and `MissingPackuments` / `MissingPackumentsRecovered` / `MissingPackumentGiveUps` metrics; `LaggyPackumentLagHigh` and `MissingPackumentsHigh` alarms |
| Laggy packuments | Process every packument the registry serves immediately; queue a re-check of the announced revision (also when the registry has no packument yet), retried with back-off for a day, then given up on | `FailedPackumentRetrievals`, `PackumentGiveUps` (by `Reason`) and `PackumentDelay` metrics; `PendingPackumentsHigh` and `LaggyPackumentLagHigh` alarms |
| Lost events | None possible; the missed versions self-heal on the package's next publish | Package canary (`Canary/SLA-Breached`) covers the probe package |
| Head regression | The follower discards its position state and reseeds at the new head when the feed head drops below its newest checkpoint | `LastSeq` metric |

Expand Down Expand Up @@ -155,6 +156,7 @@ A consumer that never re-reads previously covered ranges has no way to notice th

- Operator runbook sections for the alarms named above: [operator-runbook.md](./operator-runbook.md)
- Follower implementation: `src/package-sources/npmjs/npm-js-follower.lambda.ts`
- Packument processor implementation: `src/package-sources/npmjs/packument-processor.lambda.ts`
- Incident that motivated the windowed scan: internal reference V2376798198 *(our observation)*

[152515]: https://github.com/orgs/community/discussions/152515
Expand Down
Loading
Loading