0

I have a go-fiber app that tries to publish messages to RabbitMQ. And here is the simplified version of the app:

// main.go
type custom_handler struct {
    RMQConn *rabbitmq.Connection
}

func newHandler(conn *rabbitmq.Connection) custom_handler {
    return custom_handler{
        RMQConn: conn,
    }
}

func main() {
    var hndlr custom_handler
    if !fiber.IsChild() {
        messageq.ConnectMQ()
        hndlr = newHandler(messageq.RMQConn)
        log.Println(messageq.RMQConn) // not nil
    }

    engine := html.New("./static", ".html")
    app := fiber.New(fiber.Config{
        Views:     engine,
        Prefork:   true,  // ATTENTION!!!
    })

    app.Use(cors.New())
    app.Get("/", func(c *fiber.Ctx) error {
        return c.Render("index", fiber.Map{})
    })

    app.Post("/", hndlr.handleFileupload)

    log.Fatal(app.Listen(":8080"))
}

func (hndlr custom_handler) handleFileupload(c *fiber.Ctx) error {
    log.Println(hndlr.RMQConn) // nil
    RMQChan, err := hndlr.RMQConn.Channel()
    if err != nil {
        return err
    }
    defer RMQChan.Close()
}

And messageq package looks like this:

// messageq.go
var RMQConn *rabbitmq.Connection

func ConnectMQ() {
    var err error
    amqpServerURL := os.Getenv("RABBITMQ_SERVER_URL")
    RMQConn, err = rabbitmq.Dial(amqpServerURL)
    if err != nil {
        panic(err)
    }
}

afaik, RabbitMQ Connections can be shared between goroutines. But when I run the app, I get this segmentation violation error:

producer_1  | panic: runtime error: invalid memory address or nil pointer dereference
producer_1  | [signal SIGSEGV: segmentation violation code=0x1 addr=0x14 pc=0x8ef6db]
imgress-producer_1  |
producer_1  | goroutine 7 [running]:
producer_1  | github.com/rabbitmq/amqp091-go.(*Connection).allocateChannel(0x0)
producer_1  |   /go/pkg/mod/github.com/rabbitmq/amqp091-go@v1.5.0/connection.go:679 +0x5b
producer_1  | github.com/rabbitmq/amqp091-go.(*Connection).openChannel(0x4f46c8?)
producer_1  |   /go/pkg/mod/github.com/rabbitmq/amqp091-go@v1.5.0/connection.go:709 +0x25
producer_1  | github.com/rabbitmq/amqp091-go.(*Connection).Channel(...)
producer_1  |   /go/pkg/mod/github.com/rabbitmq/amqp091-go@v1.5.0/connection.go:736
producer_1  | main.ValidateAndPublish({0xc0000120c8, 0x1, 0xb7aeff?}, 0x1?, {0xc0000e7f38, 0x8}, 0xffffffffffffffff?)
producer_1  |   /app/validate.go:14 +0x8a
producer_1  | main.custom_handler.handleFileupload({0x0?, 0x0?}, 0xc00015f080?)
producer_1  |   /app/main.go:94 +0x4bb
producer_1  | github.com/gofiber/fiber/v2.(*App).next(0xc000221b80, 0xc00009e000)
producer_1  |   /go/pkg/mod/github.com/gofiber/fiber/v2@v2.39.0/router.go:132 +0x1c2
producer_1  | github.com/gofiber/fiber/v2.(*Ctx).Next(0xc0001bcf18?)
producer_1  |   /go/pkg/mod/github.com/gofiber/fiber/v2@v2.39.0/ctx.go:945 +0x53
producer_1  | github.com/gofiber/fiber/v2/middleware/cors.New.func1(0xc00009e000)
producer_1  |   /go/pkg/mod/github.com/gofiber/fiber/v2@v2.39.0/middleware/cors/cors.go:141 +0x286
producer_1  | github.com/gofiber/fiber/v2.(*App).next(0xc000221b80, 0xc00009e000)
producer_1  |   /go/pkg/mod/github.com/gofiber/fiber/v2@v2.39.0/router.go:132 +0x1c2
producer_1  | github.com/gofiber/fiber/v2.(*App).handler(0xc000221b80, 0x485df7?)
producer_1  |   /go/pkg/mod/github.com/gofiber/fiber/v2@v2.39.0/router.go:159 +0x45
producer_1  | github.com/valyala/fasthttp.(*Server).serveConn(0xc0001478c0, {0xc8dd90?, 0xc000012070})
producer_1  |   /go/pkg/mod/github.com/valyala/fasthttp@v1.40.0/server.go:2311 +0x1268
producer_1  | github.com/valyala/fasthttp.(*workerPool).workerFunc(0xc0000000a0, 0xc0000400a0)
producer_1  |   /go/pkg/mod/github.com/valyala/fasthttp@v1.40.0/workerpool.go:224 +0xa9
producer_1  | github.com/valyala/fasthttp.(*workerPool).getCh.func1()
producer_1  |   /go/pkg/mod/github.com/valyala/fasthttp@v1.40.0/workerpool.go:196 +0x38
producer_1  | created by github.com/valyala/fasthttp.(*workerPool).getCh
producer_1  |   /go/pkg/mod/github.com/valyala/fasthttp@v1.40.0/workerpool.go:195 +0x1b0

I am assuming because I do Prefork: true while creating the app, every request will be handled in a separate goroutine, and it seems that at the moment handler can not access the connection.

So, can somebody point to something that I am missing? How can I make the RabbitMQ connection available for the handler?

Eziz Durdyyev
  • 1,110
  • 2
  • 16
  • 34
  • `prefork` doesn't run from different goroutines, it runs from different processes entirely. I don't know if that's going to be a problem, but `ConnectMQ` isn't checking the error, nor is it assigning the connection to the package level `RMQConn` variable. – JimB Dec 13 '22 at 16:01
  • @JimB I simplified and removed some files and lines for the sake of readablity. And also (most importantly), when I remove `prefork` everything works just fine. – Eziz Durdyyev Dec 13 '22 at 16:07
  • `messageq.RMQConn` will still always be `nil` in your example. – JimB Dec 13 '22 at 16:09
  • @JimB it is `nil` only inside the handler, I confirmed it. – Eziz Durdyyev Dec 13 '22 at 16:54
  • How did you verify it? What is the actual code? In the example here you never assign anything to `messageq.RMQConn`. – JimB Dec 13 '22 at 16:55
  • @JimB `RMQConn, err := rabbitmq.Dial(amqpServerURL)` inside the `messageq.go` does the job. – Eziz Durdyyev Dec 13 '22 at 16:57
  • no it doesn't, you are using `:=`, which declares a new variable within the function scope. See https://stackoverflow.com/questions/26125143/invalid-memory-address-error-when-running-postgres-queries/26125716#26125716, https://stackoverflow.com/questions/34195360/how-to-use-global-var-across-files-in-a-package?noredirect=1&lq=1, https://stackoverflow.com/questions/29462940/storing-database-connection-in-a-global-variable?noredirect=1&lq=1, etc... – JimB Dec 13 '22 at 17:05
  • @JimB, hey Jim, I am really sorry for the typo. In the actual code base I have `=`. Again sorry... – Eziz Durdyyev Dec 13 '22 at 17:12
  • You still are not showing if you are checking the error, and how that might be handled. However it looks like you are only connecting the DB if `!fiber.IsChild()`, so none of the child processes are going to have connections. – JimB Dec 13 '22 at 17:14
  • @JimB it panics when error occures. Yes I am guessing so too, as I wrote it in the question. So how can I make the connection available for handler? – Eziz Durdyyev Dec 13 '22 at 17:23
  • 1
    I don't understand why you are blocking creating the connection with `!fiber.IsChild()`. If the child process needs to connect to the database, you need to create a connection. – JimB Dec 13 '22 at 17:24
  • @JimB, honestly, my idea was to create only one connection and use it everywhere.. but after removing `IsChild` it looks like working.. – Eziz Durdyyev Dec 13 '22 at 18:24
  • 1
    You can't use it everywhere when "everywhere" includes separate processes. In reality, first determine if you are actually scaling to many thousands of independent concurrent connections (which I doubt since this is listening on 8080, implying a proxy is involved) so that you need to load balance runtime processes across cores using `SO_REUSEPORT`. – JimB Dec 13 '22 at 18:31

0 Answers0