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)
}