Home > Software engineering >  runtime: goroutine stack exceeds 1000000000-byte limit
runtime: goroutine stack exceeds 1000000000-byte limit

Time:10-20

I have booking SAAS system in which I have thousands of merchants which run their business over this platform.

To complete their bookings on daily basis I have a cron which runs every 5 mins. In this cron, I have a scheduler api which is called inside main go routine but completed independently using another go routine along with execute command. I suddenly started getting this error on my cron system.

runtime: goroutine stack exceeds 1000000000-byte limit

Here is my code structure:


package cron

import (
    "gopkg.in/robfig/cron.v3"
)
func RunCron() {
    c := cron.New()
    c.AddFunc("@every 0h5m0s", SendBookingMail)
    c.Start()
}
func SendBookingMail() {
    // this function get all merchants & issue curl command for api url for each merchant. and then the below function is executed. 
}

func sendMailCron() {
    completeBkMailData := struct {
        Booking         models.Booking    `json:"booking"`
        TestCustomerIds []int             `json:"test_customer_ids"`
        SmsPermission   bool              `json:"sms_permission"`
        SmsKeys         map[string]string `json:"sms_keys"`
    }{
        booking,
        testCids,
        smsPermission,
        smsKeys,
    }
    b, err := json.Marshal(completeBkMailData)
    if err != nil {
        fmt.Println(err)
    }
    jsonString := string(b)
    command := "https://example.com/booking-mail"
    StartCurlCommand(command, "POST", jsonString)
}

func StartCurlCommand(url, reqType, jsonData string, headers ...string) error {
   var ip, userAgent, bearerToken string
   var cmd *exec.Cmd
   if len(headers) > 0 {
      ip = headers[0]
      userAgent = headers[1]
      bearerToken = headers[2]
   }
   if reqType == "POST" {
      cmd = exec.Command("curl", "-H", "Connection: close", "--no-keepalive", "-H", "Content-Type: application/json", "-X", "POST", "-d", jsonData, url)
   } else {
      cmd = exec.Command("curl", "-H", "Connection: close", "--no-keepalive", url)
   }
   var out bytes.Buffer
   var stderr bytes.Buffer
   cmd.Stdout = &out
   cmd.Stderr = &stderr
   err := cmd.Start()
   if err == nil {
      go func(cmd *exec.Cmd) {
         _ = cmd.Wait()
      }(cmd)
   }
   return err
}

I have already searched for this and found that there can be some sort of recursion in the code. But I am not able to identify where it is. Please help what is wrong here ?

CodePudding user response:

. But I am not able to identify where it is.

That might be because issue 7181 has yet to be released.

Any way to vote on this? Our dev team just spent a day trying to guess which part of our code might have been in the stack because our entire codebase was elided from the trace.

commit 3a81338 was reverted, but using a patched Go with this commit, just for testing, would help: instead of printing massive stack traces during endless recursion, which spams users and aren't useful, it now prints out the top and bottom 50 frames.

That would go a long way to help identify the root cause for the recursion.

The OP Amandeep Kaur confirms in the comments:

There was recursion mentioned in the stack trace.
I fixed that. Now the system is working fine.

CodePudding user response:

goroutine stack exceeds 1000000000-byte limit means you have infinite recursion (or too deep recursion) in your program. Call stack is a limited resource, so recursion should be used sparingly.

Example:

package main

func test(x int) int {
    return x   test(x 1)
}

func main() {
    test(1)
}

Print a panic:

$ go run .
runtime: goroutine stack exceeds 1000000000-byte limit
runtime: sp=0xc020160398 stack=[0xc020160000, 0xc040160000]
fatal error: stack overflow

runtime stack:
runtime.throw(0x474d4b, 0xe)
        /usr/local/go/src/runtime/panic.go:1116  0x72
runtime.newstack()
        /usr/local/go/src/runtime/stack.go:1067  0x78d
runtime.morestack()
        /usr/local/go/src/runtime/asm_amd64.s:449  0x8f

goroutine 1 [running]:
main.test(0xffffdf, 0x0)
        /home/test/gtest/test.go:3  0x50 fp=0xc0201603a8 sp=0xc0201603a0 pc=0x45dcd0
main.test(0xffffde, 0x0)
        /home/test/gtest/test.go:4  0x2f fp=0xc0201603c8 sp=0xc0201603a8 pc=0x45dcaf
main.test(0xffffdd, 0x0)
        /home/test/gtest/test.go:4  0x2f fp=0xc0201603e8 sp=0xc0201603c8 pc=0x45dcaf
main.test(0xffffdc, 0x0)
        /home/test/gtest/test.go:4  0x2f fp=0xc020160408 sp=0xc0201603e8 pc=0x45dcaf
main.test(0xffffdb, 0x0)
        /home/test/gtest/test.go:4  0x2f fp=0xc020160428 sp=0xc020160408 pc=0x45dcaf
main.test(0xffffda, 0x0)
        /home/test/gtest/test.go:4  0x2f fp=0xc020160448 sp=0xc020160428 pc=0x45dcaf
main.test(0xffffd9, 0x0)
        /home/test/gtest/test.go:4  0x2f fp=0xc020160468 sp=0xc020160448 pc=0x45dcaf
main.test(0xffffd8, 0x0)
        /home/test/gtest/test.go:4  0x2f fp=0xc020160488 sp=0xc020160468 pc=0x45dcaf
main.test(0xffffd7, 0x0)
        /home/test/gtest/test.go:4  0x2f fp=0xc0201604a8 sp=0xc020160488 pc=0x45dcaf
. . .

From the goroutine trace we can see that the issue is in test.go at line 4, which is the recursive call to line 3. That should give us enough knowledge to fix our code.

  • Related