github.com/Laplace-Game-Development/Laplace-Entangled-Environment@v0.0.3/internal/schedule/schedule.go (about)

     1  // The schedule package takes care of any "scheduled tasks". Using a combination of
     2  // Cron and ZeroMQ, the schedule package distributes the consumption of tasks to
     3  // multiple workers in order to speed up work as much as possible.
     4  package schedule
     5  
     6  import (
     7  	"fmt"
     8  	"log"
     9  
    10  	"github.com/Laplace-Game-Development/Laplace-Entangled-Environment/internal/event"
    11  	"github.com/Laplace-Game-Development/Laplace-Entangled-Environment/internal/redis"
    12  	"github.com/mediocregopher/radix/v3"
    13  	"github.com/robfig/cron/v3"
    14  )
    15  
    16  // A Cron Event represents a function that should be run
    17  // at a certain schedule. This structure should be used
    18  // when scheduling events at the onset of the application
    19  type CronEvent struct {
    20  	Schedule string
    21  	Event    func()
    22  }
    23  
    24  // Ledger of Cron Events
    25  // This shouldn't be changed by the code. Used when initially scheduling
    26  // functions to be run.
    27  var initialCronLedger []CronEvent = []CronEvent{
    28  	{"5 * * * * *", eventCheckHealth},
    29  }
    30  
    31  //// Global Variables | Singletons
    32  
    33  // Cron scheduling reference. This should only be used by this module.
    34  // if you want to dynamically schedule (for whatever reason) use this
    35  var mainCronInst *cron.Cron = nil
    36  
    37  ///////////////////////////////////////////////////////////////////////////////////////////////////
    38  ////
    39  //// Scheduler Core Functionality
    40  ////
    41  ///////////////////////////////////////////////////////////////////////////////////////////////////
    42  
    43  // ServerTask Startup Function for Cron Scheduling. Takes care of initialization.
    44  func StartCronScheduler() (func(), error) {
    45  	err := initialSchedule()
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  
    50  	return cleanUpCronScheduler, nil
    51  }
    52  
    53  // CleanUp Function returned by Startup function. Stops all Cron scheduling and reports
    54  // errors that occur when doing so.
    55  func cleanUpCronScheduler() {
    56  	log.Println("Cleaning Cron Jobs!")
    57  	ctx := mainCronInst.Stop()
    58  	select {
    59  	case <-ctx.Done():
    60  		log.Println(ctx.Err())
    61  	default:
    62  	}
    63  	log.Println("Cron Jobs Clean!")
    64  }
    65  
    66  // Schedule Initialization called from StartCronScheduler. Goes through the
    67  // "initialCronLedger" and adds each entry to the Cron scheduling reference.
    68  // It also intitializes the Cron scheduling instance.
    69  func initialSchedule() error {
    70  	mainCronInst = cron.New(cron.WithSeconds())
    71  
    72  	for i, cronEvent := range initialCronLedger {
    73  		_, err := mainCronInst.AddFunc(cronEvent.Schedule, cronEvent.Event)
    74  		if err != nil {
    75  			log.Printf("Error Reached at Cron Event Index: %d\n", i)
    76  			return err
    77  		}
    78  	}
    79  
    80  	return nil
    81  }
    82  
    83  ///////////////////////////////////////////////////////////////////////////////////////////////////
    84  ////
    85  //// Schedule Events -- Cron Subscribed Functions
    86  ////
    87  ///////////////////////////////////////////////////////////////////////////////////////////////////
    88  
    89  // Function added through the "initialCronLedger." Pops entries off of a list representing
    90  // possibly old and unused games that need their data cleaned up. Send the data to Workers.
    91  // See healthTaskWork to see how this data is used.
    92  func eventCheckHealth() {
    93  	gameIDSlice := make([]string, EventHealthTaskCapacity)
    94  	gameIDSlicePrefixed := make([]string, EventHealthTaskCapacity)
    95  
    96  	err := redis.MainRedis.Do(radix.Cmd(&gameIDSlice, "LRANGE", event.HealthTaskQueue, "0", fmt.Sprintf("%d", EventHealthTaskCapacity-1)))
    97  	if err != nil {
    98  		log.Fatalln("Trouble Using Health Event: " + err.Error())
    99  	}
   100  
   101  	err = redis.MainRedis.Do(radix.Cmd(nil, "LTRIM", event.HealthTaskQueue, fmt.Sprintf("%d", EventHealthTaskCapacity), "-1"))
   102  	if err != nil {
   103  		log.Fatalln("Trouble Using Health Event: " + err.Error())
   104  	}
   105  
   106  	if len(gameIDSlice) == 0 {
   107  		return
   108  	}
   109  
   110  	for i, s := range gameIDSlice {
   111  		gameIDSlicePrefixed[i] = constructTaskWithPrefix(HealthTaskPrefix, s)
   112  	}
   113  
   114  	err = SendTasksToWorkers(gameIDSlicePrefixed...)
   115  	if err != nil {
   116  		log.Fatalf("Trouble Using Health Event! Error: %v", err.Error())
   117  	}
   118  }