Index the event-tier columns the sweeps, event log and retention scan (closes #314)
check / check (push) Failing after 2s
check / check (push) Failing after 2s
The per-webhook tables declared no secondary indexes, so the recovery and sweep queries (by delivery status, every minute), the event log (deliveries by event, results by delivery) and retention (events by age) each scanned a whole table. Add indexes through GORM model tags so AutoMigrate creates them on a fresh and on an existing per-webhook database. events.created_at is indexed by overriding the embedded BaseModel field on Event alone, leaving the other tables' created_at unindexed. A test drops the indexes from an opened database, reopens it, and asserts the open recreated them. The README Data Model section lists the indexes. Model: opus-4-8
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
package database_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"sneak.berlin/go/webhooker/internal/database"
|
||||
)
|
||||
|
||||
// indexedColumn names a secondary index by the model and struct field
|
||||
// GORM derives the index name from.
|
||||
type indexedColumn struct {
|
||||
model any
|
||||
field string
|
||||
}
|
||||
|
||||
// eventTierIndexes are the columns the background work reads by: the
|
||||
// recovery and sweep queries (status), the event log (event_id and
|
||||
// delivery_id) and retention (created_at).
|
||||
var eventTierIndexes = []indexedColumn{
|
||||
{&database.Delivery{}, "Status"},
|
||||
{&database.Delivery{}, "EventID"},
|
||||
{&database.DeliveryResult{}, "DeliveryID"},
|
||||
{&database.Event{}, "CreatedAt"},
|
||||
}
|
||||
|
||||
// TestWebhookDBManager_OpenAddsEventTierIndexes verifies that opening a
|
||||
// per-webhook database that predates these indexes creates them, so the
|
||||
// queries above stop scanning whole tables. It stands in for an older
|
||||
// database file by dropping the indexes AutoMigrate just created, then
|
||||
// reopening the same file.
|
||||
func TestWebhookDBManager_OpenAddsEventTierIndexes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mgr, lc := setupTestWebhookDBManager(t)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, lc.Start(ctx))
|
||||
|
||||
defer func() { require.NoError(t, lc.Stop(ctx)) }()
|
||||
|
||||
webhookID := uuid.New().String()
|
||||
|
||||
db, err := mgr.GetDB(webhookID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// A fresh database has them.
|
||||
for _, ix := range eventTierIndexes {
|
||||
require.True(t, db.Migrator().HasIndex(ix.model, ix.field))
|
||||
}
|
||||
|
||||
// Stand in for a database file created before the indexes existed.
|
||||
for _, ix := range eventTierIndexes {
|
||||
require.NoError(t, db.Migrator().DropIndex(ix.model, ix.field))
|
||||
require.False(t, db.Migrator().HasIndex(ix.model, ix.field))
|
||||
}
|
||||
|
||||
// Drop the cached connection so the next open reopens the file and
|
||||
// runs AutoMigrate against it, as a restart would.
|
||||
require.NoError(t, mgr.CloseAll())
|
||||
|
||||
db, err = mgr.GetDB(webhookID)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, ix := range eventTierIndexes {
|
||||
assert.True(t, db.Migrator().HasIndex(ix.model, ix.field),
|
||||
"opening the existing database should create the index on %s",
|
||||
ix.field)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user