Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CountEstimate is not safe to be called from transaction context #1945

Open
Ezian opened this issue Mar 8, 2022 · 0 comments
Open

CountEstimate is not safe to be called from transaction context #1945

Ezian opened this issue Mar 8, 2022 · 0 comments

Comments

@Ezian
Copy link

Ezian commented Mar 8, 2022

Expected Behavior

CountEstimate(threshold int) should be safely called from any context, including transaction.

Current Behavior

If a transaction is run containing a CountEstimate() instruction without preliminary run of other query using this function, the transaction will fail.

It is due to the fact that CountEstimate() use an error-based lazy loading in count_estimate.go

if err != nil {
  if pgerr, ok := err.(internal.PGError); ok && pgerr.Field('C') == "42883" {
    // undefined_function
    err = q.createCountEstimateFunc()
    // ...
}

This lazy-loading cause a rollback of the transaction, which fails.

Possible Solution

Several possible solutions:

  1. Change the "lazy init" of this function by checking it's existence whenever we use the function CountEstimate()... But can lead to performance issue...
  2. Handle the specific case of transaction whenever we fails to create this function
  3. Don't create the pgCountEstimateFunc from the query db whenever it's a transaction, but from the baseDB of the transaction...
  4. Make it a feature, add some docs to the function with the workaround ^^

Steps to Reproduce

(Error handling is omitted)

func main(){
  dbh, _ := getDBHandler() // return a db handler (*pg.DB) on a "clean" db (without _go_pg_count_estimate_v2 function)
  
  tx, _ := db.Begin()
  _, err := tx.Model(&SomeModel{}).CountEstimate(10)
  if err != nil {
     return panic(fmt.Sprintf("countEstimate failure. %s", err))
  }
}

This code will panic with message:
panic: countEstimate failure. ERROR #25P02 current transaction is aborted, commands ignored until end of transaction block

The code below will succeed, since the first query will create the _go_pg_count_estimate_v2, and avoid the error in the transaction

func main(){
  dbh, _ := getDBHandler() // return a db handler (*pg.DB) on a "clean" db (without _go_pg_count_estimate_v2 function)

  // This will create the _go_pg_count_estimate_v2 function, which will make it available in above transaction
  db.Model(&SomeModel{}).CountEstimate(10)
  
  tx, _ := db.Begin()
  _, err := tx.Model(&SomeModel{}).CountEstimate(10)
  if err != nil {
     return panic(fmt.Sprintf("countEstimate failure. %s", err))
  }
}

Context (Environment)

  • Posgresql version : 14.1
  • go-pg version : v10.10.6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant