Please see the following code. This is a piece of code that simulates database operations with a loop inside.
In this loop, each database operation requires a timeout context of 2 seconds.
My question is that when the number of loops is very large, the 12th line will create a lot of contexts, and the 13th line will leave a lot of defer codes to be executed. Is this the proper way to use Context? Is there a better solution?
I believe a reasonable next step is to just put the body of the for rows.Next() { ... } loop into its own function. The simplest thing would be to just wrap it into a func() { ... }() immediately-invoked closure to force the defers to run after each iteration:
// handle each row in this loop
for rows.Next() {
func() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) // Line 11
defer cancel() //Line 12
_, err := db.ExecContext(ctx, "......") // Please ignore the specific SQL statement and args.
if err != nil {
// error handle
}
//...
}() // immediately invoked closure causes `defer` to run each loop
// iteration.
}
Depending on the complexity within the // ... comments, it may make more sense to move the block in the loop into its own function or functions.
All that being said…
I’d ask: Why are you calling ExecContext in a loop over QueryContext results? Isn’t there a JOIN or some other construct within the RDBMS where you could do this? This looks like an n + 1 query to me.