sijms / go-ora

Pure go oracle client
MIT License
786 stars 174 forks source link

PGA memory leak with LOBs #439

Closed nenadnoveljic closed 1 year ago

nenadnoveljic commented 1 year ago

The problem appears when go-ora is used with jmoiron/sqlx. The problem doesn't appear with godror.

Test case:

package main

import (
    "fmt"
    "log"

    "github.com/jmoiron/sqlx"
    go_ora "github.com/sijms/go-ora/v2"
)

const HOST = "localhost"
const PORT = 1521
const SERVICE_NAME = "XE"
const USER = 
const PASSWORD = 

func getMyUsedPGA(db *sqlx.DB) (float64, error) {
    var pga float64
    err := db.Get(&pga, `SELECT 
    sum(p.pga_used_mem)
FROM   v$session s,
    v$process p
WHERE  s.paddr = p.addr AND 
s.sid = userenv('SID')`)
    return pga, err
}

func main() {
    databaseUrl := go_ora.BuildUrl(HOST, PORT, SERVICE_NAME, USER, PASSWORD, map[string]string{})
    db, err := sqlx.Open("oracle", databaseUrl)
    if err != nil {
        log.Fatalf("failed to connect %s", err)
    }

    for i := 0; i < 10; i++ {
                var result go_ora.Clob

        sql := "SELECT to_clob('A') FROM dual"
        err := db.Get(&result, sql)
        if err != nil {
            log.Fatalf("failed to execute the query %s", err)
        }

        //log.Printf("Result %v", r)
        p, err := getMyUsedPGA(db)
        if err != nil {
            log.Fatalf("failed to get memory %s", err)
        }
        fmt.Printf("PGA [bytes]: %f \n", p)
    }
}

Output:

PGA [bytes]: 2745511.000000 
PGA [bytes]: 2868151.000000 
PGA [bytes]: 2999175.000000 
PGA [bytes]: 3130199.000000 
PGA [bytes]: 3261223.000000 
PGA [bytes]: 3392247.000000 
PGA [bytes]: 3523271.000000 
PGA [bytes]: 3654295.000000 
PGA [bytes]: 3785319.000000 
PGA [bytes]: 3916343.000000 
nenadnoveljic commented 1 year ago

But this (without jmoiron/sqlx) doesn't leak:

func main() {
    databaseUrl := go_ora.BuildUrl(HOST, PORT, SERVICE_NAME, USER, PASSWORD, map[string]string{})
    db, err := sqlx.Open("oracle", databaseUrl)
    conn, err := go_ora.NewConnection(databaseUrl)
    if err != nil {
        log.Fatalf("failed to connect %s", err)
    }

    err = conn.Open()
    if err != nil {
        log.Fatalf("failed to open %s", err)
    }

    defer func() {
        err = conn.Close()
        if err != nil {
            fmt.Println("Can't close 2nd connection: ", err)
        }
    }()

    if err != nil {
        log.Fatalf("failed to connect %s", err)
    }

    for i := 0; i < 10; i++ {
        sqlText := `BEGIN SELECT to_clob('A') s INTO :1 FROM dual; END;`
        var data go_ora.Clob

        _, err = conn.Exec(sqlText, go_ora.Out{Dest: &data, Size: 100000})
        if err != nil {
            log.Fatalf("failed to execute %s", err)
        }
        //fmt.Printf("data: %s \n", data.String)

        p, err := getMyUsedPGA(db)
        if err != nil {
            log.Fatalf("failed to get memory %s", err)
        }
        fmt.Printf("PGA [bytes]: %f \n", p)
    }
}
go run main.go
data: A 
PGA [bytes]: 2546447.000000 
data: A 
PGA [bytes]: 2538063.000000 
data: A 
PGA [bytes]: 2538063.000000 
data: A 
PGA [bytes]: 2538063.000000 
data: A 
PGA [bytes]: 2538063.000000 
data: A 
PGA [bytes]: 2538063.000000 
data: A 
PGA [bytes]: 2538063.000000 
data: A 
PGA [bytes]: 2538063.000000 
data: A 
PGA [bytes]: 2538063.000000 
data: A 
PGA [bytes]: 2538063.000000