Background Jobs & Scheduled Tasks
Relevant source files
The following files were used as context for generating this wiki page:
The OnChainSage backend utilizes a system of background jobs and scheduled tasks to perform routine maintenance, data processing, and notification delivery. These tasks are primarily managed using the @nestjs/schedule module, which provides cron-based scheduling, and in some cases, the cron library directly. This ensures that critical operations like analytics aggregation, signal lifecycle management, and user communication occur automatically without direct user interaction.
The system is designed to be modular, with scheduled tasks residing within their relevant feature modules (e.g., AnalyticsService, DigestService). This co-location of logic improves maintainability and clarity. The TasksModule serves as a dedicated module for certain tasks, such as signal management.
System Architecture
Scheduled tasks are implemented as methods within NestJS services, decorated with @Cron. The NestJS ScheduleModule discovers these methods at runtime and executes them according to their defined cron schedule. Some services, like EngagementReportingService, instantiate CronJob objects directly for more complex scheduling scenarios.
The following diagram illustrates the high-level architecture of the scheduled task system, showing the key services and their roles.
This diagram shows how the main ScheduleModule triggers cron jobs defined in various services across the application.
Sources: apps/backend/src/analytics/analytics.service.ts, apps/backend/src/digest/digest.service.ts, apps/backend/src/tasks/signal.task.ts, apps/backend/src/social-engagement-analytics/enagagemanet-analytics/services/engagement-reporting.service.ts
Scheduled Tasks Summary
The following table summarizes the primary background jobs running in the system.
| Task Description | Service | Method | Schedule | Cron Expression |
|---|---|---|---|---|
| Expire old trading signals | SignalTaskService | handleCron | Every minute | CronExpression.EVERY_MINUTE |
| Update daily and summary analytics | AnalyticsService | updateAnalytics | Every day at midnight | CronExpression.EVERY_DAY_AT_MIDNIGHT |
| Send email digests to users | DigestService | sendDigests | Every day at 8:00 AM | 0 8 * * * |
| Generate daily engagement report | EngagementReportingService | setupScheduledReports | Every day at midnight | 0 0 * * * |
| Generate weekly engagement report | EngagementReportingService | setupScheduledReports | Every Monday at 1:00 AM | 0 1 * * 1 |
| Generate monthly engagement report | EngagementReportingService | setupScheduledReports | 1st of month at 2:00 AM | 0 2 1 * * |
Sources: apps/backend/src/tasks/signal.task.ts:14, apps/backend/src/analytics/analytics.service.ts:74, apps/backend/src/digest/digest.service.ts:25, apps/backend/src/social-engagement-analytics/enagagemanet-analytics/services/engagement-reporting.service.ts:68-84
Detailed Task Breakdowns
Signal Expiration Task
This task is responsible for managing the lifecycle of trading signals by marking old signals as expired.
Sources: apps/backend/src/tasks/signal.task.ts, apps/backend/src/tasks/tasks.module.ts
Implementation
- Service:
SignalTaskService - Schedule: Every minute (
CronExpression.EVERY_MINUTE) - Logic: The
handleCronmethod is triggered by the scheduler. It logs the execution and calls thesignalsService.expireOldSignals()method to perform the actual expiration logic.
The sequence diagram below shows the flow for the signal expiration task.
Analytics Update Task
This nightly job processes on-chain events from the previous day to generate daily and summary analytics.
Sources: apps/backend/src/analytics/analytics.service.ts
Implementation
- Service:
AnalyticsService - Schedule: Every day at midnight (
CronExpression.EVERY_DAY_AT_MIDNIGHT) - Logic: The
updateAnalyticsmethod orchestrates a multi-step process:- Calls
updateDailyAnalyticsfor the previous day, calculating metrics like new signals, new votes, active users, and amounts staked/unstaked. - Calls
updateAnalyticsSummaryto recalculate platform-wide totals (total users, signals, votes, staked amount). - Calls
markEventsAsProcessedto flag the events from the previous day, preventing them from being processed again.
- Calls
The following flowchart illustrates the analytics update process.
Email Digest Task
This task sends personalized email digests to users based on their preferences, summarizing recent high-confidence trading signals.
Sources: apps/backend/src/digest/digest.service.ts
Implementation
- Service:
DigestService - Schedule: Every day at 8:00 AM (
0 8 * * *) - Logic: The
sendDigestsmethod fetches all user digest preferences, iterates through them, and for each enabled user:- Fetches top signals from the last 24 hours.
- Generates an HTML digest using the
MailService. - Sends the email.
- Logs the digest delivery in the
DigestLogentity.
The sequence diagram below details the digest sending process.
Engagement Reporting Tasks
This service uses the cron library directly to schedule multiple report generation tasks for social engagement analytics.
Implementation
- Service:
EngagementReportingService - Schedules: Daily, Weekly, and Monthly.
- Logic: The
setupScheduledReportsmethod is called in the constructor. It creates and registers three distinctCronJobinstances:- A daily job at midnight (
0 0 * * *). - A weekly job on Mondays at 1 AM (
0 1 * * 1). - A monthly job on the 1st of the month at 2 AM (
0 2 1 * *).
- A daily job at midnight (
- Each job calls
generateAndDeliverScheduledReport, which generates a report (as a PDF by default), saves it to the/reportsdirectory on the file system, and logs the path.
// apps/backend/src/social-engagement-analytics/enagagemanet-analytics/services/engagement-reporting.service.ts:68-88
private setupScheduledReports(): void {
// Daily report at midnight
const dailyJob = new CronJob('0 0 * * *', () => {
this.logger.log('Generating daily engagement report');
this.generateAndDeliverScheduledReport(ReportType.DAILY);
});
// Weekly report on Mondays at 1am
const weeklyJob = new CronJob('0 1 * * 1', () => {
this.logger.log('Generating weekly engagement report');
this.generateAndDeliverScheduledReport(ReportType.WEEKLY);
});
// Monthly report on the 1st of each month at 2am
const monthlyJob = new CronJob('0 2 1 * *', () => {
this.logger.log('Generating monthly engagement report');
this.generateAndDeliverScheduledReport(ReportType.MONTHLY);
});
// Register the cron jobs
this.schedulerRegistry.addCronJob('dailyEngagementReport', dailyJob);
this.schedulerRegistry.addCronJob('weeklyEngagementReport', weeklyJob);
this.schedulerRegistry.addCronJob('monthlyEngagementReport', monthlyJob);
// Start the jobs
dailyJob.start();
weeklyJob.start();
monthlyJob.start();
}