mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
7.2 KiB
7.2 KiB
Content Processing Implementation
This document outlines the implementation of the new event-driven content processing system for Omnivore.
Overview
The implementation moves from a direct queue-based approach to an event-driven architecture where:
- User saves a link → Library item created in
Processingstate - ContentSaveRequestedEvent emitted → Event queued for processing
- Content worker processes event → Fetches and processes content using content-fetch service
- Library item updated → State changes to
SucceededorFailed
Architecture Components
State Machine
[Requested] → [Processing] → [ContentFetched] → [ContentParsed] → [Succeeded]
↓ ↓
[Failed] ← [Processing] (retry)
Core Components
-
Event System (
packages/api/src/events/)ContentSaveRequestedEvent: Event data structure with validationEventManager: Handles event routing and queue management
-
Content Worker (
packages/api/src/workers/)ContentWorker: Main worker class handling content processing eventscontent-processing-service.ts: Content processing logic by typecontent-worker-helpers.ts: Helper functions for labels, thumbnails, rules
-
Content Type Detection (
packages/api/src/utils/)content-type-detector.ts: Determines content type from URL and MIME type
-
Integration Tests (
packages/api/test/integration/)content-processing.test.ts: End-to-end tests for the complete workflow
Implementation Details
Content Processing Flow
-
Link Saving (
create_page_save_request.ts):// Create library item in processing state const libraryItem = await createOrUpdateLibraryItem({...}) // Emit content save requested event await eventManager.emit(new ContentSaveRequestedEvent({ userId, libraryItemId, url, contentType, metadata })) -
Event Processing (
ContentWorker):// Process content based on type switch (contentType) { case ContentType.HTML: processedContent = await processHtmlContent(url, metadata) break // ... other types } // Update library item with results await updateLibraryItem(libraryItemId, processedContent, userId) -
Content Fetching (
content-processing-service.ts):// Uses existing content-fetch service const fetchResult = await fetchContentFromService(url, locale, timezone) // Processes and returns structured content return { title, author, description, content, wordCount, siteName, thumbnail, itemType, contentHash, }
Supported Content Types
- HTML: Web articles and pages
- PDF: Document files with text extraction
- EMAIL: Email content processing
- RSS: RSS/Atom feed items
- YOUTUBE: Video content with transcript extraction
Error Handling and Retries
- Exponential backoff: 2s initial delay, 3 retry attempts
- Graceful degradation: Failed processing sets item state to
Failed - Fallback mechanism: Direct queue enqueueing if event system fails
Performance Features
- Concurrent processing: Worker handles multiple jobs simultaneously
- Queue management: Configurable concurrency and rate limiting
- Caching: Leverages existing content-fetch caching
- Resource cleanup: Proper worker shutdown and cleanup
Testing Strategy
Integration Tests
The implementation includes comprehensive integration tests covering:
- End-to-end workflow: Link saving → processing → completion
- Content type handling: HTML, PDF, email, RSS, YouTube
- Error scenarios: Network failures, invalid content, retries
- Performance testing: Concurrent processing of multiple items
- State transitions: Proper state management throughout lifecycle
Test Structure
describe('Content Processing Integration', () => {
// Setup test user, worker, and mocks
it('should create library item in processing state when user saves link')
it('should emit ContentSaveRequestedEvent when library item is created')
it('should process content when event is handled')
it('should handle PDF content processing')
it('should handle processing failures gracefully')
it('should support retry mechanism for failed processing')
it('should process multiple items concurrently')
})
Migration Strategy
Phase 1: Parallel Implementation ✅
- New event-driven system runs alongside existing queue system
- Event emission with fallback to direct enqueueing
- Comprehensive testing of new system
Phase 2: Gradual Migration
- Enable event system for new content processing
- Monitor performance and reliability
- Migrate existing queue jobs to event system
Phase 3: Legacy Removal
- Remove direct queue enqueueing code
- Clean up old content processing jobs
- Update documentation and deployment scripts
Configuration
Environment Variables
# Content processing
CONTENT_FETCH_URL=http://content-fetch-service:3000
CONTENT_FETCH_TOKEN=your-service-token
# Queue configuration
REDIS_URL=redis://localhost:6379
MQ_REDIS_URL=redis://localhost:6379
# Feature flags
CONTENT_FETCH_QUEUE_ENABLED=true
Queue Configuration
// Event routing configuration
{
queueName: 'content-processing',
jobName: 'process-content-save',
jobOptions: {
attempts: 3,
backoff: { type: 'exponential', delay: 2000 },
removeOnComplete: 100,
removeOnFail: 50,
}
}
Monitoring and Observability
Logging
- Structured logging with context (user ID, library item ID, URL)
- Performance metrics (processing time, queue depth)
- Error tracking with detailed error messages
Health Checks
GET /health
{
"status": "ok",
"timestamp": "2024-01-15T10:30:00Z",
"contentWorker": {
"isRunning": true,
"queueName": "content-processing",
"processedJobs": 1234,
"failedJobs": 12
}
}
Metrics
- Content processing success/failure rates
- Processing time by content type
- Queue depth and throughput
- Worker performance and resource usage
Benefits
- Simplified Architecture: Clear separation of concerns with event-driven design
- Better Scalability: Independent scaling of content processing workers
- Improved Reliability: Proper error handling, retries, and state management
- Enhanced Testability: Comprehensive test coverage with realistic scenarios
- Rich User Experience: Better state management and user feedback
- Maintainability: Cleaner code organization and easier debugging
Future Enhancements
- Priority Processing: Different priorities based on content type and user tier
- Content Enrichment: AI-powered summarization and metadata extraction
- Batch Processing: Efficient handling of bulk imports
- Real-time Updates: WebSocket notifications for processing status
- Advanced Caching: Intelligent content caching strategies
- Analytics Integration: Content processing analytics and insights
This implementation provides a robust foundation for content processing that can scale with Omnivore's growth while maintaining a rich user experience.