From 59f3008f1ed33431849e2aa6ff195262d1ce84cd Mon Sep 17 00:00:00 2001 From: pasha1coil Date: Wed, 10 Jul 2024 16:56:54 +0300 Subject: [PATCH] add benchs for pagination --- benchmarks/pagination_test.go | 329 ++++++++++++++++++++++++++++++++++ 1 file changed, 329 insertions(+) create mode 100644 benchmarks/pagination_test.go diff --git a/benchmarks/pagination_test.go b/benchmarks/pagination_test.go new file mode 100644 index 0000000..96fb4fc --- /dev/null +++ b/benchmarks/pagination_test.go @@ -0,0 +1,329 @@ +package benchmarks + +import ( + "database/sql" + "log" + "testing" + + _ "github.com/lib/pq" +) + +const ( + accountID = "64f2cd7a7047f28fdabf6d9e" + connStr = "host=localhost port=35432 user=squiz password=Redalert2 dbname=squiz sslmode=disable" + queryTotal = ` + WITH user_data AS ( + SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $1 AND accountsAmo.Deleted = false + ) + SELECT f.*, COUNT(*) OVER() as total_count + FROM fields f JOIN user_data u ON f.AccountID = u.AmoID + WHERE f.Deleted = false + ORDER BY f.ID OFFSET ($2 - 1) * $3 LIMIT $3; + ` + queryCount = ` + WITH user_data AS ( + SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $1 AND accountsAmo.Deleted = false + ) + SELECT COUNT(*) + FROM fields f JOIN user_data u ON f.AccountID = u.AmoID + WHERE f.Deleted = false; + ` + queryData = ` + WITH user_data AS ( + SELECT AmoID FROM accountsAmo WHERE accountsAmo.AccountID = $1 AND accountsAmo.Deleted = false + ) + SELECT f.* + FROM fields f JOIN user_data u ON f.AccountID = u.AmoID + WHERE f.Deleted = false + ORDER BY f.ID OFFSET ($2 - 1) * $3 LIMIT $3; + ` +) + +type GetFieldsWithPaginationRow struct { + ID int64 `db:"id" json:"id"` + Amoid int32 `db:"amoid" json:"amoid"` + Code string `db:"code" json:"code"` + Accountid int32 `db:"accountid" json:"accountid"` + Name string `db:"name" json:"name"` + Entity interface{} `db:"entity" json:"entity"` + Type interface{} `db:"type" json:"type"` + Deleted bool `db:"deleted" json:"deleted"` + Createdat sql.NullTime `db:"createdat" json:"createdat"` + TotalCount int64 `db:"total_count" json:"total_count"` +} + +func initDB() *sql.DB { + db, err := sql.Open("postgres", connStr) + if err != nil { + log.Fatal(err) + } + return db +} + +// Все получаем в одном запросе не аллоцируя при этом массив +func BenchmarkAllOne(b *testing.B) { + db := initDB() + defer db.Close() + for i := 0; i < b.N; i++ { + page := 1 + size := 25 + rows, err := db.Query(queryTotal, accountID, page, size) + if err != nil { + b.Fatal(err) + } + defer rows.Close() + + var results []GetFieldsWithPaginationRow + for rows.Next() { + var row GetFieldsWithPaginationRow + if err := rows.Scan( + &row.ID, + &row.Amoid, + &row.Code, + &row.Accountid, + &row.Name, + &row.Entity, + &row.Type, + &row.Deleted, + &row.Createdat, + &row.TotalCount, + ); err != nil { + b.Fatal(err) + } + results = append(results, row) + } + + if err := rows.Err(); err != nil { + b.Fatal(err) + } + } +} + +// Все получаем в одном запросе аллоцируя при этом массив +func BenchmarkAllOnePreAllocation(b *testing.B) { + db := initDB() + defer db.Close() + for i := 0; i < b.N; i++ { + page := 1 + size := 25 + rows, err := db.Query(queryTotal, accountID, page, size) + if err != nil { + b.Fatal(err) + } + defer rows.Close() + + results := make([]GetFieldsWithPaginationRow, size) + for rows.Next() { + var row GetFieldsWithPaginationRow + if err := rows.Scan( + &row.ID, + &row.Amoid, + &row.Code, + &row.Accountid, + &row.Name, + &row.Entity, + &row.Type, + &row.Deleted, + &row.Createdat, + &row.TotalCount, + ); err != nil { + b.Fatal(err) + } + results = append(results, row) + } + + if err := rows.Err(); err != nil { + b.Fatal(err) + } + } +} + +// Считается сначала количество потом получаются данные длину и емкость массиву не меняем +func BenchmarkCountThenGetData(b *testing.B) { + db := initDB() + defer db.Close() + for i := 0; i < b.N; i++ { + page := 1 + size := 25 + + row := db.QueryRow(queryCount, accountID) + var totalCount int + if err := row.Scan(&totalCount); err != nil { + b.Fatal(err) + } + var results []GetFieldsWithPaginationRow + rows, err := db.Query(queryData, accountID, page, size) + if err != nil { + b.Fatal(err) + } + defer rows.Close() + + for rows.Next() { + var row GetFieldsWithPaginationRow + if err := rows.Scan( + &row.ID, + &row.Amoid, + &row.Code, + &row.Accountid, + &row.Name, + &row.Entity, + &row.Type, + &row.Deleted, + &row.Createdat, + ); err != nil { + b.Fatal(err) + } + results = append(results, row) + } + + if err := rows.Err(); err != nil { + b.Fatal(err) + } + } +} + +// Параллельное вычисление данных и общего количество при этом длина слайса = size +func BenchmarkParallel(b *testing.B) { + db := initDB() + defer db.Close() + + for i := 0; i < b.N; i++ { + page := 1 + size := 25 + results := make([]GetFieldsWithPaginationRow, size) + channel := make(chan error, 2) + + go func() { + row := db.QueryRow(queryCount, accountID) + var totalCount int + channel <- row.Scan(&totalCount) + }() + + go func() { + rows, err := db.Query(queryData, accountID, page, size) + if err != nil { + channel <- err + return + } + defer rows.Close() + + index := 0 + for rows.Next() { + if err := rows.Scan( + &results[index].ID, + &results[index].Amoid, + &results[index].Code, + &results[index].Accountid, + &results[index].Name, + &results[index].Entity, + &results[index].Type, + &results[index].Deleted, + &results[index].Createdat, + ); err != nil { + channel <- err + return + } + index++ + } + channel <- rows.Err() + }() + + for i := 0; i < 2; i++ { + if err := <-channel; err != nil { + b.Fatal(err) + } + } + } +} + +// Считается сначала количество потом получаются данные создаем слайс через маке указывая ему длину начальную кап = лен +func BenchmarkWithPreAllocation(b *testing.B) { + db := initDB() + defer db.Close() + + for i := 0; i < b.N; i++ { + page := 1 + size := 25 + results := make([]GetFieldsWithPaginationRow, size) + + row := db.QueryRow(queryCount, accountID) + var totalCount int + if err := row.Scan(&totalCount); err != nil { + b.Fatal(err) + } + rows, err := db.Query(queryData, accountID, page, size) + if err != nil { + b.Fatal(err) + } + defer rows.Close() + + index := 0 + for rows.Next() { + if err := rows.Scan( + &results[index].ID, + &results[index].Amoid, + &results[index].Code, + &results[index].Accountid, + &results[index].Name, + &results[index].Entity, + &results[index].Type, + &results[index].Deleted, + &results[index].Createdat, + ); err != nil { + b.Fatal(err) + } + index++ + } + + if err := rows.Err(); err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkWithPreAllocationAndMonitoringTotalCount(b *testing.B) { + db := initDB() + defer db.Close() + + for i := 0; i < b.N; i++ { + page := 1 + size := 50 + + row := db.QueryRow(queryCount, accountID) + var totalCount int + if err := row.Scan(&totalCount); err != nil { + b.Fatal(err) + } + if totalCount < size { + size = totalCount + } + results := make([]GetFieldsWithPaginationRow, size) + rows, err := db.Query(queryData, accountID, page, size) + if err != nil { + b.Fatal(err) + } + defer rows.Close() + + index := 0 + for rows.Next() { + if err := rows.Scan( + &results[index].ID, + &results[index].Amoid, + &results[index].Code, + &results[index].Accountid, + &results[index].Name, + &results[index].Entity, + &results[index].Type, + &results[index].Deleted, + &results[index].Createdat, + ); err != nil { + b.Fatal(err) + } + index++ + } + + if err := rows.Err(); err != nil { + b.Fatal(err) + } + } +}