You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat: re-read the history of a credly template released from hold - #5167
A template released from hold (or from excluded) has a history of badges that the live poll never saw: nothing emitted while it was held. Without this, LFC131 and the event badges would only ever get their new badges once mapped.
Refs linuxfoundation/lfx-cdp-insights-ops#940. Stacked on #5166 (queue table, DAL, template filter).
What
Resolve script
applyDecisions queues a release for every template whose decision reaches mapped:* without being mapped before (held:*, excluded, or a template new to the mapping table), inside the same transaction as the decision row. A run that dies in between leaves the template held, and the next run decides it again. A template Credly creates after go-live has badges held between its birth and the resolve that maps it: its release covers that gap from created_at.
Release floor = the template's created_at from Credly, never earlier than CREDLY_HISTORY_START (2019-03-01) and never later than the top.
--release <badge template id> queues one by hand (full history, no Credly/Snowflake round trip). It refuses a template that is not mapped to a live integration, since the poll would complete such a release without reading anything.
The apply log carries releasesQueued.
badgePoll
After the live window is caught up, and before the global backfill, the poll takes the next pending release and walks that template alone in 7-day state_updated_at windows with filter[badge_template_id], newest first, cursor saved on the release row after every page.
Same page cap as the rest of the run; complete: false while a release is pending.
A cursor save that touches 0 rows means the release was re-queued under our feet: the walker drops the stale bounds and reads the queue again.
A release queued while a run is in progress is newer than the routing loaded at the run start: an unknown template triggers one fresh routing read from the database before the release is skipped. Unmapped or unreadable releases are completed with a warning instead of blocking the queue and the backfill.
A window that Credly keeps failing is halved and both halves are walked, as in the global backfill.
A shadow run (emitEnabled: false) never touches the queue.
Listing carries created_at for the floor.
Tests
releasesToQueue: held→mapped, excluded→mapped with clamped floor, new→mapped from creation date, fallback to history start.
runBadgePoll releases: template filter walk, halved window, routing reloaded for a release queued mid-run, unmapped completed, page cap mid-release, release before global backfill, stale walk dropped, unreadable bounds, shadow run untouched.
createCredlyDeps: routing memoised per instance, re-read on request.
452 tests green, pnpm tsc-check and pnpm lint --deny-warnings clean.
Medium Risk
Changes Credly sync ordering and emits historical badge activities for newly mapped templates; transactional queueing limits partial-state risk, but incorrect release bounds or routing could emit or skip large badge volumes.
Overview
When a Credly badge template is newly mapped (from hold, excluded, or first-time mapping), the resolve flow now queues a history release in the same DB transaction as the mapping decision, with a time floor from the template's created_at (clamped to CREDLY_HISTORY_START). --release <template id> on resolve-credly-templates queues a full-history release manually for an already-mapped template.
runBadgePoll processes pending releases after the live window is caught up and before global backfill: it pages Credly with filter[badge_template_id] in 7-day windows, persists progress on the release row, completes or skips bad/unmapped entries, reloads routing when a mid-run mapping appears, and treats complete: false while a release is still pending. Shadow runs ignore the queue. CredlyDeps gains release queue helpers and optional fresh template routing reload; template listing includes created_at for release floors.
Reviewed by Cursor Bugbot for commit 968eca3. Bugbot is set up for automated code reviews on this repo. Configure here.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Critical release-handling bugs can stall release and global backfill by repeatedly processing the lower half of a window and permanently skip history when cached routing lacks a newly mapped target.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
The routing reload reuses a cached snapshot, so newly mapped templates can be skipped and their history jobs permanently completed without fetching badges; a cache-bypassing refresh and real-deps regression coverage are needed.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Unresolved critical issues in release timing, status checks, reactivation handling, and routing can leave badge history unrecovered or send badges to the wrong segment.
No open issues 🎉
5 previously missed issues
In code that hasn't changed since last review
Queue a release when reactivating a deactivated mapping
This excludes reactivated mappings because their before label still starts with mapped:, even though buildPlan marks them as changed. The live poll held their badges while deactivatedAt was set, but applying the decision clears that field without queuing the missed history. Preserve the prior activation state and queue a release when a deactivated mapping becomes active again.
Determine release eligibility from the locked template status
Release eligibility comes from the earlier plan snapshot rather than the row locked by the upsert. If another resolve changes an unlocked template from mapped to held after that snapshot, this apply maps it again without queuing a release, leaving the held-period history unrecovered. Determine the previous status under the upsert's lock and queue the release in that same transaction.
Refresh release top after upserting the template decision
Release tops are calculated before the sequential apply loop. If earlier decisions or database locks delay a template beyond the five-minute poll lag, a live poll can pass over badges newer than that top while the template is still held, leaving them outside both the release and future live windows. Refresh the top after upserting this template's decision, inside the same transaction.
Refresh routing for known templates on each release generation
An unlocked template can be remapped and manually re-queued during the live walk, but this check retains its cached old target because the template ID is already present. The release then emits and stores previously unseen badges on the old segment and completes; a failed mapping lock only logs a warning. Refresh routing once per release generation, including for known template IDs.
Recheck pending releases before completing the backfill
A release queued during the global backfill is absent from releasePending, so finishing that backfill can return complete: true with a release still waiting. This schedules the next poll at the 720-minute cadence instead of the short retry. Recheck the queue before returning, while preserving the shadow-run guard.
The release walk's skip path (unmapped template, unreadable bounds) only
advanced counters.pagesFetched when it actually fetched a page from Credly,
so a run with several releases queued for templates that later became
unmapped or excluded could spin through completeRelease/nextRelease pairs
bounded only by the wall-clock run budget, never reaching the live or
backfill loops.
Refs linuxfoundation/lfx-cdp-insights-ops#940
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Human assessment is needed because concurrency and release-window risks could leave skipped badges unreplayed, while the transaction guarantees lack direct test coverage.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A template released from hold (or from
excluded) has a history of badges that the live poll never saw: nothing emitted while it was held. Without this, LFC131 and the event badges would only ever get their new badges once mapped.Refs linuxfoundation/lfx-cdp-insights-ops#940. Stacked on #5166 (queue table, DAL, template filter).
What
Resolve script
applyDecisionsqueues a release for every template whose decision reachesmapped:*without being mapped before (held:*,excluded, or a template new to the mapping table), inside the same transaction as the decision row. A run that dies in between leaves the template held, and the next run decides it again. A template Credly creates after go-live has badges held between its birth and the resolve that maps it: its release covers that gap fromcreated_at.created_atfrom Credly, never earlier thanCREDLY_HISTORY_START(2019-03-01) and never later than the top.--release <badge template id>queues one by hand (full history, no Credly/Snowflake round trip). It refuses a template that is not mapped to a live integration, since the poll would complete such a release without reading anything.releasesQueued.badgePoll
state_updated_atwindows withfilter[badge_template_id], newest first, cursor saved on the release row after every page.complete: falsewhile a release is pending.emitEnabled: false) never touches the queue.Listing carries
created_atfor the floor.Tests
releasesToQueue: held→mapped, excluded→mapped with clamped floor, new→mapped from creation date, fallback to history start.runBadgePollreleases: template filter walk, halved window, routing reloaded for a release queued mid-run, unmapped completed, page cap mid-release, release before global backfill, stale walk dropped, unreadable bounds, shadow run untouched.createCredlyDeps: routing memoised per instance, re-read on request.pnpm tsc-checkandpnpm lint --deny-warningsclean.Rollout
pnpm script:resolve-credly-templates --release 067f5afd-160d-42df-961e-31d19e117173(CKAD on umbertoTest5.1), watchcredly_badge_poll_runforreleasePages/releases.🤖 Generated with Claude Code