core/benchmarks/pagination_test.go

330 lines
7.4 KiB
Go
Raw Normal View History

2024-07-10 13:56:54 +00:00
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)
}
}
}