Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance cron job configuration with timezone and retry options #3750

Open
wants to merge 4 commits into
base: staging
Choose a base branch
from

Conversation

Istiak-A-Tashrif
Copy link

@Istiak-A-Tashrif Istiak-A-Tashrif commented Oct 23, 2024

added timezone configuration and retry mechanisms to the cron job in src/device-registry/bin/jobs/check-active-statuses.js for improved reliability.

Summary by CodeRabbit

  • New Features

    • Enhanced job for checking active device statuses with a retry mechanism.
    • Job renamed to reflect its focus on active status verification.
    • Improved logging for device status checks, including detailed error messages and counts.
  • Bug Fixes

    • Enhanced error handling during status checks with retries.
  • Chores

    • Job scheduling now includes timezone configuration, defaulting to 'UTC'.

Copy link
Contributor

coderabbitai bot commented Oct 23, 2024

📝 Walkthrough
📝 Walkthrough

Walkthrough

The changes in this pull request primarily focus on enhancing the checkActiveStatuses function within src/device-registry/bin/jobs/check-active-statuses.js. Key modifications include the addition of a retry mechanism for status checks, integration of timezone management using moment-timezone, and adjustments to the job scheduling process. The function now attempts to check device statuses up to three times with a specified delay, while maintaining the original logic for counting statuses. Detailed error logging and improved control flow are also implemented to ensure robust execution.

Changes

File Path Change Summary
src/device-registry/bin/jobs/check-active-statuses.js - Added imports and constants for timezone, max retries, and retry delay.
- Updated function to include a retry mechanism with error logging.
- Modified job scheduling to include timezone configuration.
src/device-registry/bin/jobs/v2-check-network-status-job.js - Renamed job to check-active-statuses-job.
- Replaced checkNetworkStatus with checkActiveStatuses method, incorporating retry logic and enhanced logging.

Possibly related issues

Possibly related PRs

Suggested labels

ready for review

Suggested reviewers

  • Baalmart
  • BenjaminSsempala

🌟 In the realm of code, where logic flows,
A retry mechanism now brightly glows.
With timezone awareness, it stands tall,
Checking statuses, never to fall.
Errors logged, with messages clear,
In this job's journey, there's nothing to fear! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 22b5a79 and 4c3fb37.

📒 Files selected for processing (1)
  • src/device-registry/bin/jobs/check-active-statuses.js (1 hunks)

});
const activeMissingStatusCount = await DeviceModel("airqo").countDocuments({
isActive: true,
status: { $exists: false } || { $eq: null } || { $eq: "" },
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct the MongoDB query for status conditions

In your MongoDB queries, the use of JavaScript logical OR operators (||) does not function as intended. MongoDB queries require the use of the $or operator to combine multiple conditions.

Apply this diff to fix the query conditions:

For lines 28~ and 49~:

- status: { $exists: false } || { $eq: null } || { $eq: "" },
+ $or: [
+   { status: { $exists: false } },
+   { status: null },
+   { status: "" }
+ ],

This change ensures that the query correctly identifies documents where the status field is missing, null, or an empty string.

Also applies to: 49-49

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (1)
src/device-registry/bin/jobs/v2-check-network-status-job.js (1)

119-121: Enhance error logging with additional context

Including the error's stack trace is helpful. Consider adding more contextual information or using a standardized error logging format to aid in debugging.

For example, you might include the function name or additional details about the failed operation.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 4c3fb37 and f7e3cf2.

📒 Files selected for processing (1)
  • src/device-registry/bin/jobs/v2-check-network-status-job.js (1 hunks)

const moment = require("moment-timezone");
const TIMEZONE = moment.tz.guess();
const UPTIME_THRESHOLD = 35;
const moment = require("moment-timezone"); // Keep using Moment.js
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider replacing Moment.js with a modern date-time library

Moment.js is in maintenance mode and has been deprecated. It's recommended to use modern alternatives like Luxon or date-fns for improved performance and future support.

Apply this diff to replace Moment.js with Luxon:

-const moment = require("moment-timezone"); // Keep using Moment.js
+const { DateTime } = require("luxon");

And update the usage at line 137:

-  const currentTime = moment.tz(TIMEZONE).format('YYYY-MM-DD HH:mm:ss');
+  const currentTime = DateTime.now().setZone(TIMEZONE).toFormat('yyyy-MM-dd HH:mm:ss');

Committable suggestion was skipped due to low confidence.

Copy link
Contributor

@Baalmart Baalmart left a comment

Choose a reason for hiding this comment

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

Thanks @Istiak-A-Tashrif , this is great and very helpful.
I have just left a few comments for your review. :)

const ACTIVE_STATUS_THRESHOLD = 0;
const { logText, logObject } = require("@utils/log");

// Configure timezone (e.g., 'UTC')
const TIMEZONE = 'UTC'; // You can change this to your desired timezone
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for this!
Can we consider using moment.tz.guess() for this? More like:
const TIMEZONE = moment.tz.guess();

This is just to ensure consistent and accurate timezone handling.

scheduled: true,
timezone: TIMEZONE, // Set the desired timezone
Copy link
Contributor

Choose a reason for hiding this comment

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

Sweet! I like the timezone aspect, much welcome. Thanks @Istiak-A-Tashrif

@Istiak-A-Tashrif
Copy link
Author

i have modified the timezone. please check it out snd accept for hacktoberfest.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between f7e3cf2 and 8e23385.

📒 Files selected for processing (1)
  • src/device-registry/bin/jobs/v2-check-network-status-job.js (1 hunks)
🔇 Additional comments (1)
src/device-registry/bin/jobs/v2-check-network-status-job.js (1)

124-134: Well-implemented error handling and retry mechanism

The error handling implementation is robust with:

  • Detailed error logging with stack traces
  • Proper retry mechanism with delays
  • Clear maximum retry limit handling

const moment = require("moment-timezone"); // Keep using Moment.js
const ACTIVE_STATUS_THRESHOLD = process.env.ACTIVE_STATUS_THRESHOLD || 0;
const { logText, logObject } = require("@utils/log");

const TIMEZONE = moment.tz.guess();
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Consider using explicit timezone configuration

Using moment.tz.guess() might lead to unexpected behavior in server environments. It's better to explicitly configure the timezone through environment variables.

Apply this diff:

-const TIMEZONE = moment.tz.guess();
+const TIMEZONE = process.env.TIMEZONE || 'UTC';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const TIMEZONE = moment.tz.guess();
const TIMEZONE = process.env.TIMEZONE || 'UTC';

Comment on lines +34 to +64
const activeIncorrectStatusResult = await DeviceModel("airqo").aggregate([
{
$match: {
isActive: true,
status: { $ne: "deployed" },
},
},
},
{
$group: {
_id: null,
totalDevices: { $sum: 1 },
offlineDevicesCount: {
$sum: {
$cond: [{ $eq: ["$isOnline", false] }, 1, 0],
},
{
$group: {
_id: "$name",
},
},
},
]);
]);

if (result.length === 0 || result[0].totalDevices === 0) {
logText("No deployed devices found");
logger.info("No deployed devices found.");
return;
}

const { totalDevices, offlineDevicesCount } = result[0];
const offlinePercentage = (offlineDevicesCount / totalDevices) * 100;
const activeMissingStatusResult = await DeviceModel("airqo").aggregate([
{
$match: {
isActive: true,
$or: [
{ status: { $exists: false } },
{ status: null },
{ status: "" }
],
},
},
{
$group: {
_id: "$name",
},
},
]);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consolidate duplicate aggregation pipelines

The aggregation pipelines for incorrect and missing status devices are very similar and could be combined to reduce database calls.

Consider combining the aggregations:

-      const activeIncorrectStatusResult = await DeviceModel("airqo").aggregate([/*...*/]);
-      const activeMissingStatusResult = await DeviceModel("airqo").aggregate([/*...*/]);
+      const deviceStatusResults = await DeviceModel("airqo").aggregate([
+        {
+          $match: {
+            isActive: true,
+            $or: [
+              { status: { $ne: "deployed" } },
+              { status: { $exists: false } },
+              { status: null },
+              { status: "" }
+            ]
+          }
+        },
+        {
+          $group: {
+            _id: "$name",
+            statusType: {
+              $cond: [
+                { $or: [
+                  { $eq: ["$status", null] },
+                  { $eq: ["$status", ""] }
+                ]},
+                "missing",
+                "incorrect"
+              ]
+            }
+          }
+        }
+      ]);
+      
+      const activeIncorrectStatusResult = deviceStatusResults.filter(doc => doc.statusType === "incorrect");
+      const activeMissingStatusResult = deviceStatusResults.filter(doc => doc.statusType === "missing");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const activeIncorrectStatusResult = await DeviceModel("airqo").aggregate([
{
$match: {
isActive: true,
status: { $ne: "deployed" },
},
},
},
{
$group: {
_id: null,
totalDevices: { $sum: 1 },
offlineDevicesCount: {
$sum: {
$cond: [{ $eq: ["$isOnline", false] }, 1, 0],
},
{
$group: {
_id: "$name",
},
},
},
]);
]);
if (result.length === 0 || result[0].totalDevices === 0) {
logText("No deployed devices found");
logger.info("No deployed devices found.");
return;
}
const { totalDevices, offlineDevicesCount } = result[0];
const offlinePercentage = (offlineDevicesCount / totalDevices) * 100;
const activeMissingStatusResult = await DeviceModel("airqo").aggregate([
{
$match: {
isActive: true,
$or: [
{ status: { $exists: false } },
{ status: null },
{ status: "" }
],
},
},
{
$group: {
_id: "$name",
},
},
]);
const deviceStatusResults = await DeviceModel("airqo").aggregate([
{
$match: {
isActive: true,
$or: [
{ status: { $ne: "deployed" } },
{ status: { $exists: false } },
{ status: null },
{ status: "" }
]
}
},
{
$group: {
_id: "$name",
statusType: {
$cond: [
{ $or: [
{ $eq: ["$status", null] },
{ $eq: ["$status", ""] }
]},
"missing",
"incorrect"
]
}
}
}
]);
const activeIncorrectStatusResult = deviceStatusResults.filter(doc => doc.statusType === "incorrect");
const activeMissingStatusResult = deviceStatusResults.filter(doc => doc.statusType === "missing");

Comment on lines +141 to +146
const schedule = '30 */2 * * *'; // At minute 30 of every 2nd hour
cron.schedule(schedule, () => {
const currentTime = moment.tz(TIMEZONE).format('YYYY-MM-DD HH:mm:ss');
logger.info(`Running job at ${currentTime}`);
checkActiveStatuses();
}, {
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Timezone inconsistency detected across cron jobs

I've found an important timezone inconsistency that needs attention:

  • Most services use explicit "Africa/Nairobi" timezone
  • v2-check-network-status-job.js and v2/v3-store-readings-job.js use dynamic timezone detection with moment.tz.guess()
  • check-active-statuses.js uses hardcoded 'UTC'

These jobs running at "30 */2 * * *" could execute at different times due to these timezone inconsistencies, potentially causing race conditions or unexpected behavior.

Recommendations:

  • Standardize timezone configuration across all cron jobs to "Africa/Nairobi"
  • Update the following files:
    1. src/device-registry/bin/jobs/v2-check-network-status-job.js
    2. src/device-registry/bin/jobs/v2-store-readings-job.js
    3. src/device-registry/bin/jobs/v3-store-readings-job.js
    4. src/device-registry/bin/jobs/check-active-statuses.js
🔗 Analysis chain

LGTM! Verify cron schedule across timezones

The cron schedule implementation looks good. The job will run every 2 hours at minute 30, with proper timezone handling and logging.

Let's verify the cron schedule usage across the codebase:


Let me verify the timezone handling across these cron jobs to ensure consistency.

These scripts will help us verify if all cron jobs are using consistent timezone handling, as timezone misconfigurations could lead to jobs running at unexpected times.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other cron schedules that might conflict
rg -l "cron.schedule" | xargs rg "(\*/2|\*/1|\*/3) \* \* \*" -A 2

Length of output: 1678


Script:

#!/bin/bash
# Search for timezone configurations in these files
rg -l "cron.schedule" | xargs rg -B2 "TIMEZONE|timezone" 

Length of output: 7868


Script:

#!/bin/bash
# Look for timezone imports or configurations
rg "moment.tz|TIMEZONE" $(rg -l "cron.schedule")

Length of output: 2393


Script:

#!/bin/bash
# Check if timezone is defined in environment configs
fd -e json -e js -e env config | xargs rg "TIMEZONE"

Length of output: 52

Comment on lines +20 to +32
const activeIncorrectStatusCount = await DeviceModel("airqo").countDocuments({
isActive: true,
status: { $ne: "deployed" },
});

const activeMissingStatusCount = await DeviceModel("airqo").countDocuments({
isActive: true,
$or: [
{ status: { $exists: false } },
{ status: null },
{ status: "" }
],
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Optimize database queries by combining count operations

The separate count queries for incorrect and missing statuses could be combined into a single aggregation pipeline for better performance.

Consider refactoring to:

-      const activeIncorrectStatusCount = await DeviceModel("airqo").countDocuments({
-        isActive: true,
-        status: { $ne: "deployed" },
-      });
-
-      const activeMissingStatusCount = await DeviceModel("airqo").countDocuments({
-        isActive: true,
-        $or: [
-          { status: { $exists: false } },
-          { status: null },
-          { status: "" }
-        ],
-      });
+      const statusCounts = await DeviceModel("airqo").aggregate([
+        {
+          $match: {
+            isActive: true
+          }
+        },
+        {
+          $group: {
+            _id: null,
+            incorrectStatus: {
+              $sum: {
+                $cond: [
+                  { $and: [
+                    { $ne: ["$status", "deployed"] },
+                    { $ne: ["$status", null] },
+                    { $ne: ["$status", ""] }
+                  ]},
+                  1,
+                  0
+                ]
+              }
+            },
+            missingStatus: {
+              $sum: {
+                $cond: [
+                  { $or: [
+                    { $eq: ["$status", null] },
+                    { $eq: ["$status", ""] }
+                  ]},
+                  1,
+                  0
+                ]
+              }
+            }
+          }
+        }
+      ]);
+      const { incorrectStatus: activeIncorrectStatusCount, missingStatus: activeMissingStatusCount } = statusCounts[0] || { incorrectStatus: 0, missingStatus: 0 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const activeIncorrectStatusCount = await DeviceModel("airqo").countDocuments({
isActive: true,
status: { $ne: "deployed" },
});
const activeMissingStatusCount = await DeviceModel("airqo").countDocuments({
isActive: true,
$or: [
{ status: { $exists: false } },
{ status: null },
{ status: "" }
],
});
const statusCounts = await DeviceModel("airqo").aggregate([
{
$match: {
isActive: true
}
},
{
$group: {
_id: null,
incorrectStatus: {
$sum: {
$cond: [
{ $and: [
{ $ne: ["$status", "deployed"] },
{ $ne: ["$status", null] },
{ $ne: ["$status", ""] }
]},
1,
0
]
}
},
missingStatus: {
$sum: {
$cond: [
{ $or: [
{ $eq: ["$status", null] },
{ $eq: ["$status", ""] }
]},
1,
0
]
}
}
}
}
]);
const { incorrectStatus: activeIncorrectStatusCount, missingStatus: activeMissingStatusCount } = statusCounts[0] || { incorrectStatus: 0, missingStatus: 0 };

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