Skip to content

[shareddump/feedbackfunctions] YouTube API performance improvements#253

Open
jamesmontemagno wants to merge 1 commit into
mainfrom
copilot/youtube-api-performance-analysis
Open

[shareddump/feedbackfunctions] YouTube API performance improvements#253
jamesmontemagno wants to merge 1 commit into
mainfrom
copilot/youtube-api-performance-analysis

Conversation

@jamesmontemagno

Copy link
Copy Markdown
Owner

Summary

Three targeted performance improvements to the YouTube API integration, plus a bonus bug fix.


1. Parallelize ProcessVideo calls with SemaphoreSlim(3)

Before: Videos were processed one-at-a-time in a foreach loop. Each call made 2+ serial HTTP round-trips to YouTube. A 10-video playlist = ~20+ serial HTTP calls.

After: New ProcessVideosBatch(IEnumerable<string> videoIds) method uses Task.WhenAll capped at 3 concurrent requests via SemaphoreSlim — reducing wall-clock time from O(N) to ~O(N/3). Both GetYouTubeFeedback and SearchVideos now use this.


2. Batch video metadata into a single API call

Before: ProcessVideo made a separate videos?part=snippet&id={singleId} call per video — N calls for N videos.

After: ProcessVideosBatch batches all video IDs (up to 50 per request, per YouTube's API limit) into a single videos?part=snippet call using the new YouTubeVideoResponseWithId/YouTubeVideoItemWithId models. Saves both latency and precious YouTube quota units (10,000/day limit).


3. Add blob caching for YouTube results

Before: GetYouTubeFeedback and GetRecentYouTubeVideos had no caching — every request hit the YouTube API live. (HackerNews already had blob caching; YouTube didn't.)

After:

  • GetYouTubeFeedback: Per-video cache with 1-hour TTL, stored in a new youtube-cache blob container. Only uncached video IDs hit the API.
  • GetRecentYouTubeVideos: Per-search-query cache with 30-minute TTL, keyed by search-{topic}-{tag}-{days}d.json.

Bonus: Remove artificial Task.Delay(1500) bug fix

YouTubeFeedbackService.cs had a hardcoded await Task.Delay(1500) inside the per-video playlist analysis loop — adding 1.5 × N seconds of dead wait for no reason. Removed.


Files Changed

File Change
shareddump/Services/Interfaces/IYouTubeService.cs Add ProcessVideosBatch to interface
shareddump/Models/YouTube/YouTubeService.cs Implement ProcessVideosBatch with batching + parallelism
shareddump/Models/YouTube/YouTubeApiModels.cs Add YouTubeVideoResponseWithId / YouTubeVideoItemWithId
shareddump/Json/YouTubeJsonContext.cs Register new API models in JSON context
shareddump/Services/Mock/MockYouTubeService.cs Implement ProcessVideosBatch in mock
feedbackfunctions/FeedbackAnalysis/FeedbackFunctions.cs Use ProcessVideosBatch, add per-video blob cache
feedbackfunctions/Feeds/ContentFeedFunctions.cs Add per-search-query blob cache to GetRecentYouTubeVideos
feedbackfunctions/Services/Storage/StorageNames.cs Add YouTubeCacheContainer constant
feedbackfunctions/Services/Storage/FeedbackStorageClients.cs Add YouTubeCacheContainer property
feedbackwebapp/Services/Feedback/YouTubeFeedbackService.cs Remove Task.Delay(1500)
feedbackflow.tests/TableInitializationServiceTests.cs Update test helper for new constructor parameter

1. Parallelize ProcessVideo calls with SemaphoreSlim(3): Replace serial
   foreach loops in GetYouTubeFeedback and SearchVideos with Task.WhenAll
   capped at 3 concurrent requests via SemaphoreSlim to reduce wall-clock
   time from O(N) to ~O(N/3).

2. Batch video metadata API calls: Add ProcessVideosBatch() to
   IYouTubeService and YouTubeService. Uses a single videos?part=snippet
   request for up to 50 IDs at once instead of N individual calls,
   saving both latency and YouTube quota units.

3. Add blob caching for YouTube results: Per-video cache (1-hour TTL)
   in GetYouTubeFeedback and per-search-query cache (30-minute TTL) in
   GetRecentYouTubeVideos, keyed in youtube-cache container. Modeled
   after the existing hackernews-cache pattern.

4. Remove artificial Task.Delay(1500) in YouTubeFeedbackService that
   added 1.5s of unnecessary wait per video during playlist analysis.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown

🚀 Staging Deployment Ready!

Your changes from commit e80c61a have been deployed to staging:

🌐 Staging URL: https://staging.feedbackflow.app

You can test your changes there before merging to production.

This comment will be updated with each new commit to this PR.

@github-actions

Copy link
Copy Markdown

Functions Staging Deployment Ready!

Your Azure Functions changes from commit e80c61a have been deployed to staging:

🔗 Functions Staging URL: https://feedbackfunctions20250414121421-staging.azurewebsites.net

You can test your API endpoints and functions there before merging to production.

This comment will be updated with each new commit to this PR.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves the YouTube integration’s wall-clock performance and API quota usage by batching video metadata requests, parallelizing comment collection with bounded concurrency, and adding blob-backed caching in the Azure Functions layer. It also removes an unnecessary UI-side delay when analyzing playlist videos.

Changes:

  • Added ProcessVideosBatch(...) to batch video snippet lookups (50 IDs/request) and fetch comments concurrently (capped with SemaphoreSlim).
  • Introduced Azure Blob Storage caching for YouTube results (per-video cache for GetYouTubeFeedback, per-query cache for GetRecentYouTubeVideos) via a new youtube-cache container.
  • Removed an artificial Task.Delay(1500) from the web app’s YouTube analysis loop.
Show a summary per file
File Description
shareddump/Services/Interfaces/IYouTubeService.cs Extends the YouTube service contract with ProcessVideosBatch.
shareddump/Models/YouTube/YouTubeService.cs Implements batched metadata retrieval and bounded-parallel comment fetching.
shareddump/Models/YouTube/YouTubeApiModels.cs Adds API models to support videos?part=snippet&id=... batch responses containing id.
shareddump/Json/YouTubeJsonContext.cs Registers new YouTube batch-response models for source-gen serialization.
shareddump/Services/Mock/MockYouTubeService.cs Adds mock implementation for ProcessVideosBatch and routes ProcessVideo through it.
feedbackfunctions/FeedbackAnalysis/FeedbackFunctions.cs Adds per-video blob cache + switches to ProcessVideosBatch for uncached IDs.
feedbackfunctions/Feeds/ContentFeedFunctions.cs Adds per-search-query blob cache for recent video search results.
feedbackfunctions/Services/Storage/StorageNames.cs Introduces YouTubeCacheContainer constant.
feedbackfunctions/Services/Storage/FeedbackStorageClients.cs Wires YouTubeCacheContainer into the shared storage client.
feedbackwebapp/Services/Feedback/YouTubeFeedbackService.cs Removes unnecessary per-video analysis delay.
feedbackflow.tests/TableInitializationServiceTests.cs Updates test helper to account for the added storage client dependency.

Copilot's findings

  • Files reviewed: 11/11 changed files
  • Comments generated: 4

Comment on lines +409 to +412
outputVideos = [.. allVideoIds
.Select(id => outputVideos.FirstOrDefault(v => v.Id == id))
.Where(v => v is not null)
.Select(v => v!)];
Comment on lines 49 to 53
public ContentFeedFunctions(
ILogger<ContentFeedFunctions> logger,
IHackerNewsService hackerNewsService,
IYouTubeService youtubeService,
IRedditService redditService,
Comment on lines 82 to +86
AuthenticationMiddleware authMiddleware,
IUserAccountService userAccountService,
ITwitterThreadCacheService twitterThreadCacheService,
IFeatureGateService featureGateService)
IFeatureGateService featureGateService,
FeedbackStorageClients storageClients)
Comment on lines +128 to +132
_logger.LogInformation("YouTube search cache hit for key {CacheKey}", cacheKey);
var cachedResponse = req.CreateResponse(HttpStatusCode.OK);
await cachedResponse.WriteAsJsonAsync(cachedVideos);
await user!.TrackUsageAsync(UsageType.FeedQuery, _userAccountService, _logger, $"Topic: {topic ?? tag}, Videos: {cachedVideos.Count} (cached)");
return cachedResponse;
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.

2 participants