github.com/Axway/agent-sdk@v1.1.101/pkg/jobs/README.md (about) 1 # SDK Jobs 2 3 The jobs library is used to coordinate tasks that run within an agent. There are 4 job types, explained below, that may be used. Single run, Retry, Interval, and Scheduled jobs. 4 5 The jobs library keeps track of all jobs that are registered and executes them appropriately. Scheduled and Interval jobs are continuous jobs that execute more than once. These 6 jobs are continuously executed according to their settings. If any of these continuous jobs begin to fail the library will pause execution of all jobs until a running status is 7 achieved again. 8 9 When using the jobs library, remember that the main process of the agent can not exit, otherwise all jobs will exit 10 11 ## Job status 12 13 The following are the possible job status values that can be returned by the GetJobStatus method 14 15 | Status | Definition | 16 |--------------|-----------------------------------------------------------------------------------------| 17 | Initializing | Returned when the has been created but not yet started | 18 | Running | Returned when a job is being executed, or between executions in a working state | 19 | Retrying | Returned only for retry job types that return an error in a call to Execute | 20 | Stopped | Returned when a continuous job is in a non-working state and is waiting to be restarted | 21 | Failed | Returned when a single run or retry job does not Execute without error | 22 | Finished | Returned when a single run or retry job Executes properly | 23 24 ## Implementing the job interface 25 26 Before registering a job, the Job interface has to be implemented for your job 27 28 ```go 29 package main 30 31 import ( 32 github.com/Axway/agent-sdk/pkg/jobs 33 ) 34 35 type MyJob struct { 36 jobs.Job // implements interface 37 } 38 39 func (j *MyJob) Status() error { 40 // continually called determining the status of any dependencies for the job 41 // returning an error means the job should not be executed 42 } 43 44 func (j *MyJob) Ready() bool { 45 // called prior to executing the job the first time 46 // return true when the job can begin execution, false otherwise 47 } 48 49 func (j *MyJob) Execute() error { 50 // called each time the job should be executed 51 // returning an error stops continuous jobs from executing 52 } 53 ``` 54 55 ## Job types 56 57 The section covers the following job types 58 59 - [Single run jobs](#single-run-jobs) 60 - [Retry jobs](#retry-jobs) 61 - [Interval jobs](#interval-jobs) 62 - [Detached interval jobs](#detached-interval-jobs) 63 - [Scheduled jobs](#scheduled-jobs) 64 - [Channel jobs](#channel-jobs) 65 66 ### Single run jobs 67 68 Single run jobs are used to run a specific task exactly once, regardless of pass or fail. 69 70 #### Register Single run job 71 72 Register the job and get its status 73 74 ```go 75 package main 76 77 import ( 78 "fmt" 79 80 "github.com/Axway/agent-sdk/pkg/jobs" 81 ) 82 83 func main() { 84 myJob := MyJob{} 85 jobID, err := jobs.RegisterSingleRunJobWithName(myJob, "My Job") 86 if err != nil { 87 panic(err) // error registering the job 88 } 89 fmt.Println(GetJobStatus(jobID)) 90 } 91 ``` 92 93 ### Retry jobs 94 95 Retry jobs are like single run jobs, but are allowed to retry a specified number of times before failing 96 97 #### Register Retry job 98 99 Register the job and get its status 100 101 ```go 102 package main 103 104 import ( 105 "fmt" 106 107 "github.com/Axway/agent-sdk/pkg/jobs" 108 ) 109 110 func main() { 111 myJob := MyJob{} 112 retries := 3 113 jobID, err := jobs.RegisterRetryJobWithName(myJob, retries, "My Job") 114 if err != nil { 115 panic(err) // error registering the job 116 } 117 fmt.Println(GetJobStatus(jobID)) 118 } 119 ``` 120 121 ### Interval jobs 122 123 Interval jobs are executed with a certain time duration between the end of one execution to the beginning of the next 124 125 #### Register Interval job 126 127 Register the job and get its status 128 129 ```go 130 package main 131 132 import ( 133 "fmt" 134 135 "github.com/Axway/agent-sdk/pkg/jobs" 136 ) 137 138 func main() { 139 myJob := MyJob{} 140 interval := 30 * time.Second 141 jobID, err := jobs.RegisterIntervalJobWithName(myJob, interval, "My Job") 142 if err != nil { 143 panic(err) // error registering the job 144 } 145 fmt.Println(GetJobStatus(jobID)) 146 } 147 ``` 148 149 ### Detached interval jobs 150 151 Detached interval jobs are just like Interval jobs except they are not stopped with other jobs fail, they run independent of the job pool 152 153 ### Scheduled jobs 154 155 Scheduled jobs are executed on a certain time frame, the previous execution has to end prior to the next execution starting. 156 157 #### Defining a schedule 158 159 Scheduled jobs use a cronjob expressions to set up their schedule. The fields are Seconds, Minutes, Hours, Day of month, Month, 160 Day of week, and Year. There are also predefined expressions that may be used. 161 162 Cron expressions are defined with the above fields in a single string each field value separated by a space. 163 164 Allowed values 165 166 | Seconds | Minutes | Hour | Day of month | Month | Day of week | Year | 167 |---------|---------|--------|--------------|--------|-------------|-------------| 168 | 0 - 59 | 0 - 59 | 0 - 23 | 1 - 31 | 1 - 12 | 0 - 6 | 1970 - 2099 | 169 170 All of the fields can also utilize the following characters within their schedule 171 172 - Asterick (*) - matches all values for this field 173 - Slash (/) - describes increments steps within the field 174 - Comma (,) - separates values within the field to match 175 - Hyphen (-) - defines a range of values within the field to match 176 177 Examples 178 179 | Expression | Description | 180 |-----------------------------|---------------------------------------------------------------------------------------| 181 | \* \* \* \* \* \* \* | Run every second | 182 | 30 5-59/15 \* \* \* \* \* | Run 30 seconds past the minute, starting at minute 5 and every 15 minutes there after | 183 | 0 0 1,5,9,15,21 \* \* \* \* | Run at hour 1, 5, 9, 15, and 21 of each day | 184 | 0 0 0 \* \* 6 \* | Run at midnight every Saturday | 185 186 Predefined expressions 187 188 | Expression | Description | Equivalent | 189 |------------|---------------------------------------------|--------------------| 190 | @hourly | Run at the top of the hour | 0 0 \* \* \* \* \* | 191 | @daily | Run at midnight every day | 0 0 0 \* \* \* \* | 192 | @weekly | Run at midnight on Sundays | 0 0 0 \* \* 0 \* | 193 | @monthly | Run at midnight on the first of every month | 0 0 0 1 \* \* \* | 194 195 #### Register Scheduled job 196 197 Register the job and get its status 198 199 ```go 200 package main 201 202 import ( 203 "fmt" 204 205 "github.com/Axway/agent-sdk/pkg/jobs" 206 ) 207 208 func main() { 209 myJob := MyJob{} 210 runHalfPastHour := "0 30 * * * * *" 211 jobID, err := jobs.RegisterScheduledJobWithName(myJob, runHalfPastHour, "My Job") 212 if err != nil { 213 panic(err) // error registering the job 214 } 215 fmt.Println(GetJobStatus(jobID)) 216 } 217 ``` 218 219 ### Channel jobs 220 221 Channel jobs are executed a single time via a go routine. It is expected that the channel job runs forever. 222 223 To allow this job to be stopped and started the implementer will provide a channel that the jobs pool can use to tell the job to stop. 224 225 #### Channel job implementation example 226 227 ```go 228 package myjob 229 230 import "github.com/Axway/agent-sdk/pkg/jobs" 231 232 type MyJob struct { 233 jobs.Job 234 workChannel chan interface{} 235 stopChannel chan interface{} 236 } 237 238 func NewMyJob(stopChannel chan interface{}) *MyJob { 239 return &MyJob{ 240 stopChannel: stopChannel, 241 workChannel: make(chan interface{}), 242 } 243 } 244 245 func (m *MyJob) Ready() bool { 246 // validate all dependencies are ready 247 return true 248 } 249 250 func (m *MyJob) Status() error { 251 // check that the job is still running without error 252 return nil 253 } 254 255 func (m *MyJob) Execute() error { 256 for { 257 select { 258 case <-m.stopChannel: 259 //stop the job 260 return nil 261 case msg := <-workChannel: 262 //do work on msg 263 } 264 } 265 } 266 267 ``` 268 269 #### Register Channel job 270 271 Register the job and get its status 272 273 ```go 274 package main 275 276 import ( 277 "fmt" 278 279 "github.com/Axway/agent-sdk/pkg/jobs" 280 ) 281 282 func main() { 283 stopChannel := make(chan interface{}) 284 myJob := NewMyJob(stopChannel) 285 jobID, err := jobs.RegisterChannelJobWithName(myJob, stopChannel, "My Job") 286 if err != nil { 287 panic(err) // error registering the job 288 } 289 fmt.Println(GetJobStatus(jobID)) 290 } 291 ``` 292 293 ## Job locks 294 295 All continuous jobs (Interval and Scheduled) create locks that the agent can use to prevent the job from running at the same time as another process or job. 296 The job will lock itself prior to calling its Execute function and unlock itself after Execute has finished 297 298 Here is an example of how to create 2 jobs that can not execute at the same time. 299 300 ```go 301 package main 302 303 import ( 304 github.com/Axway/agent-sdk/pkg/jobs 305 ) 306 307 type FirstJob struct { 308 jobs.Job // implements interface 309 } 310 311 func (j *FirstJob) Status() error { 312 ... 313 } 314 315 func (j *FirstJob) Ready() bool { 316 ... 317 } 318 319 func (j *FirstJob) Execute() error { 320 ... 321 } 322 323 type SecondJob struct { 324 jobs.Job // implements interface 325 firstJobID string 326 } 327 328 func (j *SecondJob) Status() error { 329 ... 330 } 331 332 func (j *SecondJob) Ready() bool { 333 ... 334 } 335 336 func (j *SecondJob) Execute() error { 337 jobs.JobLock(j.firstJobID) 338 defer jobs.JobUnlock(j.firstJobID) 339 ... 340 } 341 342 func main() { 343 myFirstJob := FirstJob{} 344 jobID, err := jobs.RegisterIntervalJob(myFirstJob, 30 * time.Second) 345 if err != nil { 346 panic(err) // error registering the job 347 } 348 349 mySecondJob := jobID{ 350 firstJobID: jobID, 351 } 352 _, err := jobs.RegisterIntervalJob(mySecondJob, 30 * time.Second) 353 if err != nil { 354 panic(err) // error registering the job 355 } 356 } 357 ```