0

I am new to golang. I am trying to do concurrent queries to mysql db with golang. I know channels can be of the type interface. When I print tableData (type map) in RunQuery function I am getting the result. I am sending tableData to ch i.e. channel of type interface. In function getdataList I am not getting any value in ch. I don't understand what I am doing wrong.

Following is my code:

package main

import (
    "database/sql"
    "fmt"
    "net/http"
    _ "github.com/go-sql-driver/mysql"
    "log"
)


var db *sql.DB

func getdataList(id int) {
        ch := make(chan interface{})
        done := make (chan bool)
        RunQuery(ch,"select id,name, last_name,first_name from persons where id= ?", id)
        go func() {
            for {
            x, ok := <-ch //I am not getting any data in channel here
            if ok {
                fmt.Println(x)
            }else {
                fmt.Println("done")
                done <- true
                return
            }

        }
        }()
    }

func RunQuery (ch chan interface{}, query string, param interface{}) {

    stmt, err := db.Prepare(query)
    if err != nil {
                panic(err.Error())
            }
    defer stmt.Close()
    rows, err := stmt.Query(param)
    columns, err := rows.Columns()
    if err != nil {
        fmt.Println("Failed to get columns", err)
        return
    }
    count := len(columns)
    tableData := make([]map[string]interface{}, 0)
    values := make([]interface{}, count)
    valuePtrs := make([]interface{}, count)
    for rows.Next() {
      for i := 0; i < count; i++ {
          valuePtrs[i] = &values[i]
      }
      rows.Scan(valuePtrs...)
      entry := make(map[string]interface{})
      for i, col := range columns {
          var v interface{}
          val := values[i]
          b, ok := val.([]byte)
          if ok {
              v = string(b)
          } else {
              v = val
          }
          entry[col] = v
      }
      tableData = append(tableData, entry)
  }
    fmt.Pritln(tableData) //here I am getting data in map
    ch <- tableData
}


func dbtest(w http.ResponseWriter, req *http.Request) {

    go getdataList(2)
    go getdataList(3)
}

func main() {
    var err error
    db, err = sql.Open("mysql", "root:@/dbName")
    if err != nil {
        panic(err.Error())  
    }
    defer db.Close()

    http.HandleFunc("/dbTest", dbtest)

    log.Fatal(http.ListenAndServe(":8080", nil))

}
Jagrati
  • 11,474
  • 9
  • 35
  • 56

1 Answers1

1

The problem with your code is that it is blocking the execution flow before data can be read from the channel. When you call RunQuery from getdataList, RunQuery tries to send data over channel ch. However, nothing is reading from ch because the code to read from it is in getdataList and it's below the call to RunQuery.

Therefore, RunQuery never returns and the goroutine to read from ch never fires. To fix, You can try running RunQuery as a goroutine as well:

func getdataList(id int) {
        ch := make(chan interface{})
        done := make (chan bool)
        // run in a goroutine
        go RunQuery(ch,"select id,name, last_name,first_name from persons where id= ?", id)
        go func() {
            for {
            x, ok := <-ch //I am not getting any data in channel here
            if ok {
                fmt.Println(x)
            }else {
                fmt.Println("done")
                done <- true
                return
            }

        }
    }()
}

There is another issue in your code. You are never closing ch. This may result in deadlock. The most ideal place to do this appears to be in RunQuery:

func RunQuery (ch chan interface{}, query string, param interface{}) {
    // ...
    ch <- tableData
    close(ch)
}
abhink
  • 8,740
  • 1
  • 36
  • 48
  • Thanks. Its working fine. I have a question though, where should i add close(ch) in my code. Also, that done is not getting printed. Will it print only when channel is closed ? – Jagrati Aug 24 '16 at 08:28
  • I am having a doubt, In `fun dbtest` if I don't use `getdataList` with go routines. Using apache benchmark I get better performance if i don't use the two `getdataList` functions with go routine. Can u explain me why? – Jagrati Aug 24 '16 at 10:41
  • 1
    I cannot say with certainty but my guess would be that with goroutines, concurrent access is forcing sql's `db` to open two connections. Without goroutines, first call to `getdataList` creates one connection, which is then used by the second call to `getdataList`. My advice would be to update `getdataList` so it takes a list of ids and fetches most of the data with a single query. In that case, `getdataList([]int{2, 3})` with an similarly updated sql query would provide even better performance. – abhink Aug 24 '16 at 11:19
  • Hey, would you mind helping me with another problem. http://stackoverflow.com/questions/39122458/get-post-data-using-golang-http-package/39122551#39122551 – Jagrati Aug 24 '16 at 12:18