Skip to content

feat: daily gql data - #3947

Merged
capJavert merged 4 commits into
mainfrom
daily-gql-data
Jun 18, 2026
Merged

feat: daily gql data#3947
capJavert merged 4 commits into
mainfrom
daily-gql-data

Conversation

@capJavert

Copy link
Copy Markdown
Contributor

No description provided.

@capJavert capJavert self-assigned this Jun 18, 2026
@pulumi

pulumi Bot commented Jun 18, 2026

Copy link
Copy Markdown

🍹 The Update (preview) for dailydotdev/api/prod (at 0743490) was successful.

✨ Neo Code Review

Routine application deployment bumping all workloads to commit `3b38b6cc`, with a safe additive DB migration adding a `color` column to `channel_highlight_definition`. Two new authenticated GraphQL endpoints (`dailyFeed`, `dailyHighlights`) are introduced. ✅ Low Risk

This deployment rolls out two new features: a dailyFeed GraphQL query (a personalisation-aware homepage feed that selects between daily_v1 and daily_cs_v1 feed configs based on the user's Snotra state, with Redis caching per user/page for 24 hours) and a dailyHighlights query (returns the top-ranked highlight of the current day per channel digest the user is subscribed to). It also adds a color column to channel_highlight_definition via the included DB migration, and exposes that field on the ChannelConfiguration GraphQL type.

The migration job (vpc-native-api-db-migration-3b38b6cc) runs ALTER TABLE channel_highlight_definition ADD COLUMN IF NOT EXISTS color text NOT NULL DEFAULT 'text-text-tertiary' — this is additive with a safe default and will not lock the table for a meaningful duration on a typical Postgres installation.

🔵 Info — The dailyFeed resolver caches feed-service responses in Redis for a full day (ONE_DAY_IN_SECONDS). Empty responses are intentionally not cached, but non-empty first-page results will be pinned for up to 24 hours per user. If feed content needs to be invalidated mid-day (e.g., a bad batch), there is no cache-busting mechanism visible in this PR — manual Redis key deletion would be required.

🔵 InfodailyHighlights uses DISTINCT ON (cd.channel) ordered by significance then recency. The significance ordering (h.significance = 0) ASC puts Unspecified (value 0) last, which is the intended behaviour — worth confirming the enum values match expectations.

Resource Changes

    Name                                                       Type                           Operation
~   vpc-native-expire-super-agent-trial-cron                   kubernetes:batch/v1:CronJob    update
~   vpc-native-worker-job-deployment                           kubernetes:apps/v1:Deployment  update
-   vpc-native-api-db-migration-ec482697                       kubernetes:batch/v1:Job        delete
~   vpc-native-daily-digest-cron                               kubernetes:batch/v1:CronJob    update
~   vpc-native-channel-digests-cron                            kubernetes:batch/v1:CronJob    update
~   vpc-native-materialize-yearly-best-post-archives-cron      kubernetes:batch/v1:CronJob    update
~   vpc-native-temporal-deployment                             kubernetes:apps/v1:Deployment  update
+   vpc-native-api-db-migration-3b38b6cc                       kubernetes:batch/v1:Job        create
~   vpc-native-check-analytics-report-cron                     kubernetes:batch/v1:CronJob    update
~   vpc-native-update-tags-str-cron                            kubernetes:batch/v1:CronJob    update
~   vpc-native-generic-referral-reminder-cron                  kubernetes:batch/v1:CronJob    update
~   vpc-native-update-achievement-rarity-cron                  kubernetes:batch/v1:CronJob    update
~   vpc-native-personalized-digest-deployment                  kubernetes:apps/v1:Deployment  update
~   vpc-native-calculate-top-readers-cron                      kubernetes:batch/v1:CronJob    update
~   vpc-native-clean-channel-highlights-cron                   kubernetes:batch/v1:CronJob    update
~   vpc-native-private-deployment                              kubernetes:apps/v1:Deployment  update
+   vpc-native-api-clickhouse-migration-3b38b6cc               kubernetes:batch/v1:Job        create
~   vpc-native-squad-posts-analytics-refresh-cron              kubernetes:batch/v1:CronJob    update
~   vpc-native-clean-stale-user-transactions-cron              kubernetes:batch/v1:CronJob    update
~   vpc-native-user-posts-analytics-refresh-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-rotate-daily-quests-cron                        kubernetes:batch/v1:CronJob    update
~   vpc-native-personalized-digest-cron                        kubernetes:batch/v1:CronJob    update
~   vpc-native-bg-deployment                                   kubernetes:apps/v1:Deployment  update
~   vpc-native-update-current-streak-cron                      kubernetes:batch/v1:CronJob    update
~   vpc-native-update-tag-materialized-views-cron              kubernetes:batch/v1:CronJob    update
~   vpc-native-user-profile-analytics-history-clickhouse-cron  kubernetes:batch/v1:CronJob    update
-   vpc-native-api-clickhouse-migration-ec482697               kubernetes:batch/v1:Job        delete
~   vpc-native-clean-zombie-user-companies-cron                kubernetes:batch/v1:CronJob    update
~   vpc-native-clean-old-notifications-cron                    kubernetes:batch/v1:CronJob    update
~   vpc-native-clean-zombie-images-cron                        kubernetes:batch/v1:CronJob    update
~   vpc-native-clean-zombie-users-cron                         kubernetes:batch/v1:CronJob    update
~   vpc-native-post-analytics-clickhouse-cron                  kubernetes:batch/v1:CronJob    update
~   vpc-native-rotate-weekly-quests-cron                       kubernetes:batch/v1:CronJob    update
~   vpc-native-post-lifecycle-state-clickhouse-cron            kubernetes:batch/v1:CronJob    update
~   vpc-native-post-analytics-history-day-clickhouse-cron      kubernetes:batch/v1:CronJob    update
~   vpc-native-sync-subscription-with-cio-cron                 kubernetes:batch/v1:CronJob    update
~   vpc-native-generate-search-invites-cron                    kubernetes:batch/v1:CronJob    update
~   vpc-native-post-analytics-achievements-cron                kubernetes:batch/v1:CronJob    update
~   vpc-native-deployment                                      kubernetes:apps/v1:Deployment  update
... and 14 other changes

@capJavert

Copy link
Copy Markdown
Contributor Author

@claude review

@claude

claude Bot commented Jun 18, 2026

Copy link
Copy Markdown

Claude finished @capJavert's task in 3m 0s —— View job


Code Review — feat: daily gql data

  • Read changed files & diff
  • Review GraphQL queries (dailyFeed, dailyHighlights)
  • Review feed config generator & caching
  • Review migration & entity changes
  • Post review feedback

Overall this is a clean, well-tested change. The FeedDailyConfigGenerator personalised/cold-start split is tidy, the migration is safe (ADD COLUMN ... NOT NULL DEFAULT, no rewrite), and color auto-maps through the existing ChannelConfiguration GraphORM mapping with no extra config. A few things worth addressing before merge:

🟡 dailyFeed declares args that have no effect

src/schema/feeds.ts (schema ~L450, resolver ~L1690) exposes ranking, unreadOnly, and version, but fetchQueryParams only reads supportedTypes:

const { config, extraMetadata } = await dailyFeedConfigGenerator.generate(ctx, {
  user_id: ctx.userId || ctx.trackingId,
  page_size: page.limit,
  offset: 0,
  cursor: page.cursor,
  allowed_post_types: args.supportedTypes,
});

The custom applyPaging is (ctx, args, page, builder) => builder, so ranking/unreadOnly/version never reach the query or the feed service — yet the doc comment says it "Mirrors the For You feed ranking". Read-post filtering still works via the resolver's default removeHiddenPosts: true, but unreadOnly as a toggle does nothing. Either wire these through (like getConfiguredFeedQueryParams does for feedV2) or drop them from the schema so the contract isn't misleading. Fix this →

🟡 Day-long cache also pins the personalised/cold-start choice

The full feed-service FeedResponse is cached per user/page for ONE_DAY_IN_SECONDS. Because the personalise-state lookup only runs on a cache miss, a user who crosses the Snotra personalisation threshold mid-day keeps their cold-start variant until the cache expires. The cache key also omits anything about the config variant. Likely acceptable for a "daily" surface, but worth confirming it's intended — and if so, a one-line comment noting the variant is effectively cached for the day would help future readers.

🔵 Cache test may be order/isolation sensitive

The "serve repeat requests from the redis cache" test (__tests__/feeds.ts) depends on the key feeds:daily:1:... not already existing in Redis. There's no per-test cleanup of this key (unlike the DB clear() calls). If the test DB/Redis isn't flushed between runs, a stale entry could make the first nock expectation never fire. Consider deleting the key in beforeEach/afterEach to keep it hermetic.

🔵 Nits

  • src/migration/...AddChannelHighlightColor.ts: per CLAUDE.md's idempotency guidance, ADD COLUMN IF NOT EXISTS (and DROP COLUMN IF EXISTS in down) makes the migration safely re-runnable.
  • dailyHighlights empty-state uses a sentinel UUID ('00000000-...') to avoid an empty IN (). It works and is tested, but a short if (!highlightIds.length) return emptyConnection early return would read more clearly and skip the GraphORM round-trip entirely.

✅ Verified looks correct

  • dailyHighlights significance ordering: (h.significance = 0) ASC then h.significance ASC correctly ranks Breaking(1) > Major(2) > Notable(3) > Routine(4) with Unspecified(0) last — matches the test asserting backend's breaking beats today's major.
  • DISTINCT ON (cd.channel) with a leading ORDER BY cd.channel ASC is valid Postgres.
  • Migration is a non-blocking ADD COLUMN with a constant default.
  • color field flows through the existing entity-aligned ChannelConfiguration mapping — no duplicate type introduced. 👍
    · daily-gql-data

@capJavert
capJavert enabled auto-merge (squash) June 18, 2026 14:01
@capJavert
capJavert merged commit 06541f2 into main Jun 18, 2026
9 checks passed
@capJavert
capJavert deleted the daily-gql-data branch June 18, 2026 14:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant