Scheduler - green-ecolution/backend GitHub Wiki

A scheduler automates the execution of tasks at predefined times or intervals. It is used to schedule recurring jobs, such as backups, report generation, or script execution, without manual intervention. Schedulers ensure that tasks run reliably and on time, improving efficiency and reducing the risk of human error in time-sensitive processes.

Creating a new scheduled task

To implement a new scheduled task, the generic scheduler should be used. The source code for this can be found in the /internal/worker/scheduler.go file. The scheduler requires a time interval specification, indicating when the task should be executed, and a function defining the task to be executed.

General scheduler tasks can be triggered in the /internal/server/http/server.go file.

There are two ways to pass the logic to the scheduler. The first option is to use the SchedulerFunc helper and pass a function directly. The second option is to implement a struct, which can be passed to the scheduler.

Example 1: Using SchedulerFunc

// define scheduled task
scheduler := worker.NewScheduler(1 * time.Minute, worker.SchedulerFunc(...))

// run scheduled task
scheduler.Run(ctx)

Example 2: Using struct

type Task struct {
    // fields here
}

func (t *Task) Do(ctx context.Context) error {
    // task logic here
    return nil
}

// define scheduled task
scheduler := worker.NewScheduler(5*time.Minute, &Task{})

// run scheduled task
go scheduler.Run(ctx)

How to stop the scheduler:

ctx, cancel := context.WithCancel(context.Background())
go scheduler.Run(ctx)

// stop scheduled task
cancel()

Already implemented scheduler tasks

This project also uses schedulers to automatically execute tasks at a specific time:

Sensor Status Update

This scheduler is run every three hours to update the status of the sensors. It retrieves all sensors, checks whether their last data received from the TTN and stored in the database is older than 72 hours and flags the sensor as offline if it was inactive during this period. This helps to quickly identify whether a sensor is still actively sending data or not.

Watering Plan Status Update

This scheduler runs once every day to update the status of watering plans. It retrieves all watering plans with the status active, planned, or unknown and checks if the planned execution date is outdated (older than 24 hours). If so, the watering plan was not executed correctly, and its status is updated to not_completed. This helps to quickly identify which watering plans were not executed as intended.

Watering Status Update of trees and tree clusters

These schedulers are run once every day to update the watering status of trees and tree clusters. For trees, the scheduler retrieves all trees and checks if their watering status is just_watered. If a tree’s last watering date is older than 24 hours, it updates the watering status. If the tree has a sensor, it calculates the current watering status using the sensor data; otherwise, it sets the status to unknown.

The same process is applied to tree clusters. For tree clusters, all trees within the cluster are checked. If a tree has a sensor, the »worst« watering status among all the trees in the cluster is applied to the cluster. If none of the trees have a sensor, the cluster's watering status is set to unknown.

The status just_watered is set for all linked tree clusters and their trees whenever a watering plan is marked as completed. This status is used to give the sensors time to recalculate and send new values, ensuring that the watering status isn’t still showing as »very dry« immediately after watering. By temporarily setting the just_watered status, the system is allowed to adjust and provide more accurate, updated feedback.

Plugin Healthcheck

This scheduler runs at a fixed interval (default is every minute) to check the heartbeats of all registered plugins. If a plugin's heartbeat is older than the specified timeout (default 5 minutes), the plugin is unregistered from the system to ensure only active plugins are kept. This is useful to ensure that only active and functioning plugins remain in the system.