Files
doormile_backend/scratch/check_booking_paging.go

105 lines
2.6 KiB
Go

//go:build ignore
// Read-only: does the console's page-by-page drain actually see every booking?
//
// GetAdminBookings runs Offset/Limit with NO ORDER BY. In Postgres that makes
// the row order across pages unspecified, so a paged drain can legally return
// the same row twice and never return another. This replays the exact 6 pages
// the console fetches and compares the union against the table.
//
// go run scratch/check_booking_paging.go
package main
import (
"fmt"
"log"
"sort"
"doormile/config"
"doormile/db"
"github.com/joho/godotenv"
)
const pageSize = 100 // what the server actually returns: min(100, requested)
type row struct{ Bookingid int }
func main() {
_ = godotenv.Load()
cfg := config.Load()
db.Connect(cfg)
if db.DB == nil {
log.Fatal("no DB connection")
}
var total int64
db.DB.Raw(`SELECT count(*) FROM pickupbookings`).Scan(&total)
pages := int((total + pageSize - 1) / pageSize)
fmt.Printf("bookings: %d, pages of %d: %d\n\n", total, pageSize, pages)
// The truth: every id that exists.
var all []row
db.DB.Raw(`SELECT bookingid FROM pickupbookings`).Scan(&all)
truth := map[int]bool{}
for _, r := range all {
truth[r.Bookingid] = true
}
// The drain, exactly as the console runs it — unordered Offset/Limit.
seen := map[int]int{}
for p := 0; p < pages; p++ {
var page []row
db.DB.Raw(`SELECT bookingid FROM pickupbookings LIMIT ? OFFSET ?`, pageSize, p*pageSize).Scan(&page)
for _, r := range page {
seen[r.Bookingid]++
}
fmt.Printf(" page %d: %d rows\n", p+1, len(page))
}
missed := []int{}
for id := range truth {
if seen[id] == 0 {
missed = append(missed, id)
}
}
dupes := []int{}
for id, n := range seen {
if n > 1 {
dupes = append(dupes, id)
}
}
sort.Sort(sort.Reverse(sort.IntSlice(missed)))
sort.Sort(sort.Reverse(sort.IntSlice(dupes)))
fmt.Printf("\nunordered drain saw %d distinct of %d\n", len(seen), len(truth))
fmt.Printf(" MISSED %d: %v\n", len(missed), head(missed, 20))
fmt.Printf(" DUPLICATED %d: %v\n", len(dupes), head(dupes, 20))
// The same drain with a deterministic order — the proposed fix.
seenOrdered := map[int]int{}
for p := 0; p < pages; p++ {
var page []row
db.DB.Raw(`SELECT bookingid FROM pickupbookings ORDER BY bookingid DESC LIMIT ? OFFSET ?`,
pageSize, p*pageSize).Scan(&page)
for _, r := range page {
seenOrdered[r.Bookingid]++
}
}
missedOrdered := 0
for id := range truth {
if seenOrdered[id] == 0 {
missedOrdered++
}
}
fmt.Printf("\nwith ORDER BY bookingid DESC: saw %d distinct, missed %d\n",
len(seenOrdered), missedOrdered)
}
func head(xs []int, n int) []int {
if len(xs) > n {
return xs[:n]
}
return xs
}