Conversation
Subscribe to api.v1.post-highlighted and dispatch in-app notifications for Breaking/Major significance highlights, with push limited to Breaking and per-user 2h Redis dedup. Adds MajorHeadlineAdded notification type, default settings, schema entry, worker, and infra subscription. Made-with: Cursor
|
🍹 The Update (preview) for dailydotdev/api/prod (at 4aca7b5) was successful. ✨ Neo ExplanationThis is a routine deployment adding a new "major headline" breaking-news notification feature, including a new GCP Pub/Sub subscription and updated container images across the cluster. Risk is low, with the only thing worth validating being the scalability of the full-user-table fan-out query when the first Breaking post is published. ✅ Low RiskThis PR introduces a new The broad deployment updates (all Deployments and CronJobs) are purely a routine image tag bump to the new commit SHA. The migration Jobs are replaced as expected for this deployment pattern (name includes the commit SHA). 🔵 Info — The 🔵 Info — There is no email template wired up ( Resource Changes Name Type Operation
~ vpc-native-update-views-cron kubernetes:batch/v1:CronJob update
~ vpc-native-user-profile-analytics-history-clickhouse-cron kubernetes:batch/v1:CronJob update
~ vpc-native-generic-referral-reminder-cron kubernetes:batch/v1:CronJob update
~ vpc-native-update-tags-str-cron kubernetes:batch/v1:CronJob update
- vpc-native-api-db-migration-a018e700 kubernetes:batch/v1:Job delete
~ vpc-native-daily-digest-cron kubernetes:batch/v1:CronJob update
~ vpc-native-update-highlighted-views-cron kubernetes:batch/v1:CronJob update
~ vpc-native-deployment kubernetes:apps/v1:Deployment update
~ vpc-native-clean-zombie-user-companies-cron kubernetes:batch/v1:CronJob update
~ vpc-native-clean-zombie-users-cron kubernetes:batch/v1:CronJob update
~ vpc-native-clean-stale-user-transactions-cron kubernetes:batch/v1:CronJob update
~ vpc-native-private-deployment kubernetes:apps/v1:Deployment update
~ vpc-native-materialize-yearly-best-post-archives-cron kubernetes:batch/v1:CronJob update
~ vpc-native-squad-posts-analytics-refresh-cron kubernetes:batch/v1:CronJob update
~ vpc-native-user-posts-analytics-refresh-cron kubernetes:batch/v1:CronJob update
~ vpc-native-update-current-streak-cron kubernetes:batch/v1:CronJob update
~ vpc-native-clean-expired-better-auth-sessions-cron kubernetes:batch/v1:CronJob update
~ vpc-native-channel-highlights-cron kubernetes:batch/v1:CronJob update
~ vpc-native-ws-deployment kubernetes:apps/v1:Deployment update
~ vpc-native-rotate-weekly-quests-cron kubernetes:batch/v1:CronJob update
~ vpc-native-clean-channel-highlights-cron kubernetes:batch/v1:CronJob update
~ vpc-native-expire-super-agent-trial-cron kubernetes:batch/v1:CronJob update
+ vpc-native-api-clickhouse-migration-d60b2736 kubernetes:batch/v1:Job create
+ vpc-native-api-db-migration-d60b2736 kubernetes:batch/v1:Job create
~ vpc-native-channel-digests-cron kubernetes:batch/v1:CronJob update
~ vpc-native-worker-job-deployment kubernetes:apps/v1:Deployment update
~ vpc-native-bg-deployment kubernetes:apps/v1:Deployment update
- vpc-native-api-clickhouse-migration-a018e700 kubernetes:batch/v1:Job delete
~ vpc-native-update-tag-materialized-views-cron kubernetes:batch/v1:CronJob update
~ vpc-native-materialize-monthly-best-post-archives-cron kubernetes:batch/v1:CronJob update
+ api-sub-api.major-headline-added-notification gcp:pubsub/subscription:Subscription create
~ vpc-native-personalized-digest-deployment kubernetes:apps/v1:Deployment update
~ vpc-native-check-analytics-report-cron kubernetes:batch/v1:CronJob update
~ vpc-native-clean-gifted-plus-cron kubernetes:batch/v1:CronJob update
~ vpc-native-update-trending-cron kubernetes:batch/v1:CronJob update
~ vpc-native-post-analytics-clickhouse-cron kubernetes:batch/v1:CronJob update
~ vpc-native-user-profile-analytics-clickhouse-cron kubernetes:batch/v1:CronJob update
~ vpc-native-hourly-notification-cron kubernetes:batch/v1:CronJob update
~ vpc-native-sync-subscription-with-cio-cron kubernetes:batch/v1:CronJob update
~ vpc-native-rotate-daily-quests-cron kubernetes:batch/v1:CronJob update
~ vpc-native-calculate-top-readers-cron kubernetes:batch/v1:CronJob update
... and 12 other changes |
Made-with: Cursor
|
@claude please review this pr |
|
Claude finished @nimrodkra's task in 1m 50s —— View job PR Review: feat/major-headlines-push-notifications
Overall this is a well-structured feature addition. The code follows existing patterns, is concise, and the test coverage is solid. A few issues worth addressing before merge: Issues1. Scalability problem: full-table user query on every highlight event (
|
- Default email channel to Muted for major_headline_added since no email template is wired up. - Include postId in major_headline_added uniqueKey to prevent cross-post collisions. - Add NaN guard when parsing significance from uniqueKey in the push worker. - Add tests for blockedPostTypes in the worker and dedup/significance paths in the push worker. Made-with: Cursor
|
Addressed @claude review feedback:
Skipped (worth a follow-up if scale becomes a concern):
|
|
Claude finished @nimrodkra's task in 1m 3s —— View job Follow-up review: addressed feedback verified ✓
All four issues from the initial review are correctly resolved:
Significance parsing hardened ( Email default corrected ( New tests — the worker test file adds Streaming follow-up — the LGTM — the PR is in good shape. Ready to merge. |
ed28c97 to
354fc04
Compare
…push-notifications
Made-with: Cursor
| if (isMajorHeadline) { | ||
| const dedupResults = await Promise.all( | ||
| users.map(async (user) => { | ||
| const acquired = await setRedisObjectIfNotExistsWithExpiry( |
There was a problem hiding this comment.
why are we dedupping here?
| if (isMajorHeadline) { | ||
| const parts = (data.notification.uniqueKey || '').split(':'); | ||
| const significanceStr = parts[1]; | ||
| const significance = parseInt(significanceStr, 10); | ||
| if ( | ||
| Number.isNaN(significance) || | ||
| significance !== PostHighlightSignificance.Breaking | ||
| ) { | ||
| return; | ||
| } | ||
| } |
There was a problem hiding this comment.
this seems fragile, we ideally skip this already on worker side
| { | ||
| topic: 'api.v1.post-highlighted', | ||
| subscription: 'api.major-headline-added-notification', | ||
| args: { ackDeadlineSeconds: 60 }, |
There was a problem hiding this comment.
why are you setting ackDeadlineSeconds?
There was a problem hiding this comment.
It means subscription will error if query takes longer, better to leave it without and use defaults if not specific reason
| .description(ctx.headline) | ||
| .avatarSource(ctx.source) | ||
| .objectPost(ctx.post, ctx.source, ctx.sharedPost) | ||
| .uniqueKey(`${ctx.post.id}:${ctx.significance}:${ctx.channel}`), |
There was a problem hiding this comment.
do we expect there will be same post.id and different significance, feels like just post id would do the trick eg. one headline per post per channel.
Not sure what channels are, maybe also just per post.
| .where( | ||
| `COALESCE(u."notificationFlags"->'major_headline_added'->>'inApp', 'subscribed') = 'subscribed'`, | ||
| ) | ||
| .getRawMany<{ userId: string }>(); |
There was a problem hiding this comment.
ideally we stream like in postAddedUserNotification worker
- Drop Major significance from notification generation; only Breaking
generates notifications. Eliminates fragile uniqueKey parsing in the
push worker.
- Stream subscribed users via processStream instead of getRawMany.
- Remove the per-user 2h Redis push dedup; Breaking is rare and
uniqueKey already prevents duplicates per post.
- Simplify uniqueKey to ${ctx.post.id} (one headline per post).
- Drop ackDeadlineSeconds: 60 from the subscription, use defaults.
- Remove unused MajorHeadlineDedup StorageKey and related dedup logic.
Made-with: Cursor
|
Thanks @capJavert! Addressed all five comments in 234787e by simplifying the architecture:
Net diff: +20 / -168. |
…push-notifications
…hub.com/dailydotdev/daily-api into feat/major-headlines-push-notifications
Summary
MajorHeadlineAddednotification type with default settings + Zod schema entry.majorHeadlineAddedTypedNotificationWorkersubscribing toapi.v1.post-highlighted. Filters significance toBreaking/Major, skips non-public/deleted/vordr posts, fans out only to opted-in users.Breakingtriggers OneSignal push (Majorstays in-app). Per-user 2h Redis dedup for Breaking pushes viamh:push:<userId>.UserActionType.DismissedMajorHeadlinesAlertsBannerfor the frontend banner persistence.Test plan
Breakinghighlight: subscribed user gets in-app + push; second highlight within 2h gets only in-app (deduped)Majorhighlight: subscribed user gets in-app onlyNotable/Routine/Unspecifiedhighlight: no notificationMajorHeadlineAdded.inApp = muted: excluded from notification audiencepnpm run lintand worker spec passApp counterpart: dailydotdev/apps#5922