github.com/blend/go-sdk@v1.20220411.3/examples/cron/load_test/main.go (about)

     1  /*
     2  
     3  Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file.
     5  
     6  */
     7  
     8  package main
     9  
    10  import (
    11  	"context"
    12  	"fmt"
    13  	"math/rand"
    14  	"os"
    15  	"sync/atomic"
    16  	"time"
    17  
    18  	"github.com/blend/go-sdk/cron"
    19  	"github.com/blend/go-sdk/logger"
    20  )
    21  
    22  const (
    23  	// N is the number of jobs to load.
    24  	N = 32
    25  
    26  	// Q is the total simulation time.
    27  	Q = 10 * time.Second
    28  
    29  	// JobRunEvery is the job interval.
    30  	JobRunEvery = 5 * time.Second
    31  
    32  	// JobTimeout is the timeout for the jobs.
    33  	JobTimeout = 3 * time.Second
    34  
    35  	// JobShortRunTime is the short run time.
    36  	JobShortRunTime = 2 * time.Second
    37  
    38  	// JobLongRunTime is the long run time (will induce a timeout.)
    39  	JobLongRunTime = 8 * time.Second
    40  )
    41  
    42  var startedCount int32
    43  var completeCount int32
    44  var expectedTimeoutCount int32
    45  var timeoutCount int32
    46  
    47  var (
    48  	_ cron.Job               = (*loadTestJob)(nil)
    49  	_ cron.ScheduleProvider  = (*loadTestJob)(nil)
    50  	_ cron.ConfigProvider    = (*loadTestJob)(nil)
    51  	_ cron.LifecycleProvider = (*loadTestJob)(nil)
    52  )
    53  
    54  type loadTestJob struct {
    55  	id      int
    56  	running bool
    57  }
    58  
    59  func (j *loadTestJob) Name() string {
    60  	return fmt.Sprintf("loadTestJob_%d", j.id)
    61  }
    62  
    63  // Config returns a job config.
    64  func (j *loadTestJob) Config() cron.JobConfig {
    65  	return cron.JobConfig{
    66  		Timeout: JobTimeout,
    67  	}
    68  }
    69  
    70  // Lifecycle implements cron.LifecycleProvider.
    71  func (j *loadTestJob) Lifecycle() cron.JobLifecycle {
    72  	return cron.JobLifecycle{
    73  		OnCancellation: j.OnCancellation,
    74  	}
    75  }
    76  
    77  func (j *loadTestJob) Execute(ctx context.Context) error {
    78  	atomic.AddInt32(&startedCount, 1)
    79  	j.running = true
    80  
    81  	var runFor time.Duration
    82  	var randValue = rand.Float64()
    83  	if randValue <= 0.5 { // 50% split between short vs. long.
    84  		runFor = JobShortRunTime
    85  	} else {
    86  		atomic.AddInt32(&expectedTimeoutCount, 1)
    87  		runFor = JobLongRunTime
    88  	}
    89  
    90  	runForElapsed := time.After(runFor)
    91  	select {
    92  	case <-runForElapsed:
    93  		j.running = false
    94  		atomic.AddInt32(&completeCount, 1)
    95  		return nil
    96  	case <-ctx.Done():
    97  		j.running = false
    98  		return nil
    99  	}
   100  }
   101  
   102  func (j *loadTestJob) OnCancellation(_ context.Context) {
   103  	atomic.AddInt32(&timeoutCount, 1)
   104  	j.running = false
   105  }
   106  
   107  func (j *loadTestJob) Status() string {
   108  	if j.running {
   109  		return "Request in progress."
   110  	}
   111  	return "Request idle."
   112  }
   113  
   114  func (j *loadTestJob) Schedule() cron.Schedule {
   115  	return cron.Every(JobRunEvery)
   116  }
   117  
   118  func main() {
   119  	jm := cron.New(
   120  		cron.OptLog(logger.Prod()),
   121  	)
   122  	defer func() {
   123  		jm.Stop()
   124  	}()
   125  
   126  	if JobLongRunTime < JobTimeout {
   127  		fmt.Printf("Long Run Time: %v is less than the Time Out: %v\n", JobTimeout, JobLongRunTime)
   128  		fmt.Printf("This will cause the Completed vs. Timed Out counts to be wrong.\n")
   129  		os.Exit(1)
   130  	}
   131  
   132  	for x := 0; x < N; x++ {
   133  		jm.LoadJobs(&loadTestJob{id: x})
   134  	}
   135  	fmt.Printf("Loaded %d Job Instances.\n", N)
   136  	fmt.Printf("Jobs run every %v\n", JobRunEvery)
   137  	fmt.Printf("Jobs run for %v/%v\n", JobShortRunTime, JobLongRunTime)
   138  	fmt.Printf("Jobs timeout %v\n", JobTimeout)
   139  	fmt.Println()
   140  
   141  	if err := jm.StartAsync(); err != nil {
   142  		logger.FatalExit(err)
   143  	}
   144  
   145  	time.Sleep(Q)
   146  
   147  	if err := jm.Stop(); err != nil {
   148  		fmt.Fprintf(os.Stderr, "error stopping job manager: %+v\n", err)
   149  		os.Exit(1)
   150  	}
   151  
   152  	// given 30 seconds total
   153  	// and running every 5 seconds
   154  	// we expect each job to run 5 times (ish)
   155  
   156  	expectedStarted := N * ((int64(Q) / int64(JobRunEvery)) - 1)
   157  	expectedCompleted := expectedStarted >> 1
   158  
   159  	fmt.Printf("Expected Jobs Started:   %d\n", expectedStarted)
   160  	fmt.Printf("Actual Jobs Started:     %d\n\n", startedCount)
   161  
   162  	fmt.Printf("Expected Jobs Completed: %d\n", expectedCompleted)
   163  	fmt.Printf("Actual Jobs Completed:   %d\n\n", completeCount)
   164  
   165  	fmt.Printf("Expected Jobs Timed Out: %d\n", expectedTimeoutCount)
   166  	fmt.Printf("Actual Jobs Timed Out:   %d\n", timeoutCount)
   167  }