package pg_test import ( "context" "testing" "time" "github.com/go-pg/pg" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) func concurrency() (int, int) { if testing.Short() { return 4, 100 } return 10, 1000 } var _ = Describe("DB timeout race", func() { var db *pg.DB var C, N int BeforeEach(func() { C, N = concurrency() N = 100 }) AfterEach(func() { pool := db.Pool() Expect(pool.Len()).To(Equal(0)) Expect(pool.IdleLen()).To(Equal(0)) err := db.Close() Expect(err).NotTo(HaveOccurred()) // Give Postgres some time to recover. time.Sleep(time.Second) }) test := func() { It("is race free", func() { perform(C, func(id int) { for i := 0; i < N; i++ { _, err := db.Exec("SELECT pg_sleep(1)") Expect(err).To(HaveOccurred()) } }) }) } Describe("dial timeout", func() { BeforeEach(func() { opt := pgOptions() opt.DialTimeout = time.Nanosecond db = pg.Connect(opt) }) test() }) Describe("read timeout", func() { BeforeEach(func() { opt := pgOptions() opt.ReadTimeout = time.Nanosecond db = pg.Connect(opt) }) test() }) Describe("write timeout", func() { BeforeEach(func() { opt := pgOptions() opt.WriteTimeout = time.Nanosecond db = pg.Connect(opt) }) test() }) }) var _ = Describe("DB race", func() { var db *pg.DB var C, N int BeforeEach(func() { db = pg.Connect(pgOptions()) err := createTestSchema(db) Expect(err).NotTo(HaveOccurred()) C, N = concurrency() }) AfterEach(func() { err := db.Close() Expect(err).NotTo(HaveOccurred()) }) It("invalid Scan is race free", func() { perform(C, func(id int) { for i := 0; i < N; i++ { var n int if i%2 == 0 { _, err := db.QueryOne(pg.Scan(&n), "SELECT 1, 2") Expect(err).To(HaveOccurred()) } else { _, err := db.QueryOne(pg.Scan(&n), "SELECT 123") Expect(err).NotTo(HaveOccurred()) Expect(n).To(Equal(123)) } } }) }) It("SelectOrInsert with OnConflict is race free", func() { perform(C, func(id int) { a := &Author{ Name: "R. Scott Bakker", } for i := 0; i < N; i++ { a.ID = 0 _, err := db.Model(a). Column("id"). Where("name = ?name"). OnConflict("DO NOTHING"). Returning("id"). SelectOrInsert(&a.ID) Expect(err).NotTo(HaveOccurred()) Expect(a.ID).NotTo(BeZero()) if i%(N/C) == 0 { err := db.Delete(a) if err != pg.ErrNoRows { Expect(err).NotTo(HaveOccurred()) } } } }) count, err := db.Model((*Author)(nil)).Count() Expect(err).NotTo(HaveOccurred()) Expect(count).To(Equal(1)) }) It("SelectOrInsert without OnConflict is race free", func() { perform(C, func(id int) { a := &Author{ Name: "R. Scott Bakker", } for i := 0; i < N; i++ { a.ID = 0 _, err := db.Model(a). Column("id"). Where("name = ?name"). Returning("id"). SelectOrInsert(&a.ID) Expect(err).NotTo(HaveOccurred()) Expect(a.ID).NotTo(BeZero()) if i%(N/C) == 0 { err := db.Delete(a) if err != pg.ErrNoRows { Expect(err).NotTo(HaveOccurred()) } } } }) count, err := db.Model((*Author)(nil)).Count() Expect(err).NotTo(HaveOccurred()) Expect(count).To(Equal(1)) }) It("WithContext is race free", func() { perform(C, func(id int) { dbWithCtx := db.WithContext(context.Background()) Expect(dbWithCtx).NotTo(BeNil()) }) }) It("WithTimeout is race free", func() { perform(C, func(id int) { dbWithTimeout := db.WithTimeout(5 * time.Second) Expect(dbWithTimeout).NotTo(BeNil()) }) }) It("context timeout is race free", func() { perform(C, func(id int) { for i := 0; i < N; i++ { func() { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() _, err := db.ExecContext(ctx, "SELECT 1") Expect(err).NotTo(HaveOccurred()) }() } }) }) It("fully initializes model table", func() { type TestTable struct { tableName struct{} `sql:"'generate_series(0, 9)'"` } perform(C, func(id int) { n, err := db.Model((*TestTable)(nil)).Count() Expect(err).NotTo(HaveOccurred()) Expect(n).To(Equal(10)) }) }) })