Skip to content

Instantly share code, notes, and snippets.

@drhodes
Created October 4, 2012 03:11
Show Gist options
  • Save drhodes/3831248 to your computer and use it in GitHub Desktop.
Save drhodes/3831248 to your computer and use it in GitHub Desktop.
crashing pq by specifying runtime.GOMAXPROCS
package main
import (
_ "github.com/bmizerany/pq"
//"runtime"
"database/sql"
"log"
"math/rand"
)
/*
with linux/x86
I've come across some weirdness when runtime.GOMAXPROCS(N) where N > 2 on a
four core machine. I'm getting crashes of 2 types. It appears to be related
to garbage collection. I'm not sure if it's even an issue with the pq package.
8< ------------------------------------------------------------------
TYPE A
throw: gosched of g0
[signal 0xb code=0x1 addr=0x0 pc=0x804ab44]
goroutine 1 [running]:
goroutine 2 [syscall]:
created by runtime.main
/usr/custom/goroot/src/pkg/runtime/proc.c:220
goroutine 3 [syscall]:
syscall.Syscall6()
/usr/custom/goroot/src/pkg/syscall/asm_linux_386.s:0 +0x27
syscall.EpollWait(0x6, 0x188430a8, 0xa, 0xa, 0xffffffff, ...)
/usr/custom/goroot/src/pkg/syscall/env_unix.go:0 +0x7d
net.(*pollster).WaitFD(0x188430a0, 0x1883f740, 0x0, 0x0, 0x0, ...)
/usr/custom/goroot/src/pkg/net/fd_linux.go:146 +0x103
net.(*pollServer).Run(0x1883f740, 0x0)
/usr/custom/goroot/src/pkg/net/fd_unix.go:216 +0xdb
created by net.newPollServer
/usr/custom/goroot/src/pkg/net/newpollserver_unix.go:33 +0x2c4
8< ------------------------------------------------------------------
TYPE B
panic: invalid memory address or nil pointer dereference
throw: panic during gc
[signal 0xb code=0x1 addr=0x0 pc=0x804ace4]
goroutine 1 [running]:
github.com/bmizerany/pq.(*conn).recv1(0x18850950, 0x188edff0, 0x5)
/home/derek/dev/gopath/src/github.com/bmizerany/pq/conn.go:292 +0x104
github.com/bmizerany/pq.(*stmt).exec(0x188eacc0, 0x188edef0, 0x2)
/home/derek/dev/gopath/src/github.com/bmizerany/pq/conn.go:479 +0x29f
github.com/bmizerany/pq.(*stmt).Exec(0x188eacc0, 0x188edef0, 0x2, 0x2, 0x0, ...)
/home/derek/dev/gopath/src/github.com/bmizerany/pq/conn.go:428 +0xbe
github.com/bmizerany/pq.(*conn).Exec(0x18850950, 0x188eac80, 0x30, 0x188edef0, 0x2, ...)
/home/derek/dev/gopath/src/github.com/bmizerany/pq/conn.go:245 +0x137
database/sql.(*DB).exec(0x188013c0, 0x188eac80, 0x30, 0x62dee0, 0x2, ...)
/usr/custom/goroot/src/pkg/database/sql/sql.go:357 +0x1af
database/sql.(*DB).Exec(0x188013c0, 0x188eac80, 0x30, 0x62dee0, 0x2, ...)
/usr/custom/goroot/src/pkg/database/sql/sql.go:335 +0x74
main.Insert(0x188013c0, 0x188edd40, 0xa, 0x188e77e0, 0x19, ...)
/home/derek/dev/gopath/src/pq-bugtest/main.go:122 +0x1eb
main.main()
/home/derek/dev/gopath/src/pq-bugtest/main.go:99 +0x327
goroutine 2 [syscall]:
created by runtime.main
/usr/custom/goroot/src/pkg/runtime/proc.c:220
goroutine 3 [syscall]:
syscall.Syscall6()
/usr/custom/goroot/src/pkg/syscall/asm_linux_386.s:0 +0x27
syscall.EpollWait(0x6, 0x188430a8, 0xa, 0xa, 0xffffffff, ...)
/usr/custom/goroot/src/pkg/syscall/env_unix.go:0 +0x7d
net.(*pollster).WaitFD(0x188430a0, 0x1883f740, 0x0, 0x0, 0x0, ...)
/usr/custom/goroot/src/pkg/net/fd_linux.go:146 +0x103
net.(*pollServer).Run(0x1883f740, 0x0)
/usr/custom/goroot/src/pkg/net/fd_unix.go:216 +0xdb
created by net.newPollServer
/usr/custom/goroot/src/pkg/net/newpollserver_unix.go:33 +0x2c4
8< ------------------------------------------------------------------
To test this you'll need to get into postgres as a superuser and create a user
and a test db.
$ psql
postgres=# CREATE USER warnerb WITH PASSWORD 'passport';
postgres=# CREATE DATABASE testdb123;
----
The following listing shows when GOMAXPROCS is set to 4,3 and 2, how many
insertions were performed prior to crashing. PASS means no crash occured
with N = 4
Trials:
1: 436
2: 1555
3: 1095
4: 4752
5: 871
6: 3604
7: 1779
8: 3151
9: 642
with N = 3
Trials:
1: 1271
2: 5972
3: 3801
4: 1061
5: 853
6: 855
7: 2541
8: 2757
9: 1064
with N = 2
Trials:
1: PASS
2: PASS
3: PASS
4: PASS
5: PASS
6: PASS
7: PASS
8: PASS
9: PASS
with N = 1
Trials:
1: PASS
2: PASS
3: PASS
4: PASS
5: PASS
6: PASS
7: PASS
8: PASS
9: PASS
!! Omitting call to GOMAXPROCS altogether !!
Trials:
1: PASS
2: PASS
3: PASS
4: PASS
5: PASS
6: PASS
7: PASS
8: PASS
9: PASS
*/
const createperson = `
create table person (
did SERIAL,
unique(did),
name varchar(255),
email varchar(255),
unique(email)
);
`
func main() {
//runtime.GOMAXPROCS(3)
cstr := `user=warnerb dbname=testdb123 dbpasswd=passport sslmode=disable`
db, err := sql.Open("postgres", cstr)
if err != nil {
log.Fatal(err)
}
defer db.Close()
// Drop the table if it exists
_, err = db.Exec(`drop table if exists person cascade;`)
if err != nil {
log.Fatal(err, "\ncan't drop table person")
}
// Create the table
_, err = db.Exec(createperson)
if err != nil {
log.Fatal(err)
}
// Insert a bunch of records.
for i := 0; i < 10000; i++ {
log.Print(i, " ")
// This is where it will fail, if it does.
err := Insert(db, RandomName(), RandomEmail())
if err != nil {
log.Fatal(err, "\nProblem inserting person")
}
}
}
func RandomName() string {
alphas := `abcdefghijklmnopqrstuvwxyz`
name := ``
for i := 0; i < 10; i++ {
name += string(alphas[rand.Int()%26])
}
return name
}
func RandomEmail() string {
return RandomName() + "@" + RandomName() + ".com"
}
func Insert(db *sql.DB, name, email string) error {
log.Println("Inserting: ", name, email)
cols := `(name, email) values ($1,$2) `
_, err := db.Exec(`INSERT INTO person `+cols, name, email)
if err != nil {
return err
}
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment