Home > Net >  Running goroutine (scheduler) to refresh the global variable in AWS lambda
Running goroutine (scheduler) to refresh the global variable in AWS lambda

Time:10-02

I'm trying to refresh the global variable in AWS lambda by spawning goroutine in global context which will refresh the variable. But every-time I'm invoking the lambda the I'm getting the same value for connection string. what I'm doing wrong ? Does the goroutine doesn't work in global context of lambda ? Behaviour is very inconsisten and cloud watch doesn't show the background jobs logs it shows only when I trigger event. Can anyone tell what is the right way to achieve the same in lambda ?

Here is the code

var mx sync.Mutex
var token string

func refresh() {
     fmt.Println("Make HTTP Call")
  time.Sleep(3*time.Second)
  fmt.Println("Call Ends")
   mx.Lock()
   defer mx.Unlock()
   token = "TEST"   strconv.FormatInt(int64(randInt(200), 10)
   schedule()
}

func schedule() {
   time.AfterFunc(1*time.Minute, func() {
         refresh()
   })
}

func init() {
  fmt.Println("Make HTTP Call")
  time.Sleep(3*time.Second)
  fmt.Println("Call Ends")

  mx.Lock()
  token = "TEST1"
  mx.Unlock()

  go schedule()
  return 

}

function processEvent() {
   fmt.Println("Variable: %s\n", token)
   return "Hello World", nil
}

function main() {
   lambda.Start(processEvent)
}

CodePudding user response:

You should provide a Seed for rand. Seed is used to initialize the default rand source , if none provided it would assume Seed(1)

package main

import (
    "fmt"
    "math/rand"
    "strconv"
    "sync"
    "time"
)

var mx sync.Mutex
var token string

func main() {
    refresh()
    fmt.Println("Hello, playground", token)
}

func refresh() {
     // here i am providing a seed. It won't work in 
     // playground as the time is always 2009-11-10 23:00:03  0000 UTC
    rand.Seed(time.Now().Unix()) 

    fmt.Println("Make HTTP Call")
    time.Sleep(3 * time.Second)
    fmt.Println("Call Ends")
    mx.Lock()
    defer mx.Unlock()
    token = "TEST"   strconv.FormatInt(int64(rand.Intn(200)), 10)

}

CodePudding user response:

If you want to use scheduler which runs periodically you cannot use time.AfterFunc it runs only once after specified time. If you want to refresh token every minute you need to use other solution

import "github.com/jasonlvhit/gocron"

init() {
    gocron.Every(1).Minute().Do(refresh)
}

Update

Maybe I don't understand your use case. But you probably misunderstood the AWS Lambda. It is not designed to run "never ending scheduler" for updating token periodically. It is designed to do the job when it is invoked and return the result and if Lambda is not used any more for some period of time, then it shutdowns.

There is also another issue with this solution, Lambda do not share runtime context. Lambda provides a secure and isolated runtime environment. So If you need share data across all Lambdas you need to look for other solution than runtime context, like global state. You also do not need to use Mutex because there is only one invoke at the time. See Lambda lifecycle

You can check validity of the token before using it, but it can slow down your performance.

package main

import (
    "fmt"
    "github.com/aws/aws-lambda-go/lambda"
    "math/rand"
    "strconv"
    "time"
)

var token Token

type Token struct {
    token string
    valid time.Time
}

func newToken(token string) Token {
    // create new token with validity one minute
    return Token{token, time.Now().Add(1 * time.Minute)}
}

func (t Token) isValid() bool {
    return t.valid.After(time.Now())
}

func refresh() {
    randToken := fetchToken()
    token = newToken(randToken)
}

func init() {
    rand.Seed(time.Now().UnixNano()) // paste seed into rand
    refresh()
}

func fetchToken() string {
    // implement your logic
    rnd := rand.Int()
    return strconv.FormatInt(int64(rnd), 10)
}

func HandleRequest() (string, error) {
    if !token.isValid() {
        refresh() // refresh token if not valid
    }

    return fmt.Sprintf("Token: %s", token.token), nil
}

func main() {
    lambda.Start(HandleRequest)
}
  • Related