diff --git a/drivers/pg/optimize.go b/drivers/pg/optimize.go index 8da0cfb8..dc34f5e3 100644 --- a/drivers/pg/optimize.go +++ b/drivers/pg/optimize.go @@ -10,60 +10,12 @@ import ( "github.com/jackc/pgx/v5/pgconn" ) -// deadTupleThreshold is the minimum fraction of dead tuples a partitioned -// parent must accumulate across its partitions before OptimizeStorage will -// vacuum it. -const deadTupleThreshold = 0.1 - -// Sum n_dead_tup and n_live_tup across every leaf partition of the parent; -const optimizeStorageStatsQuery = ` - SELECT - COALESCE(SUM(stat.n_dead_tup), 0), - COALESCE(SUM(stat.n_live_tup), 0) - FROM pg_partition_tree($1::regclass) tree - LEFT JOIN pg_stat_user_tables stat ON stat.relid = tree.relid - WHERE tree.isleaf -` - type optimizeStorageConn interface { Exec(ctx context.Context, sql string, arguments ...any) (pgconn.CommandTag, error) - QueryRow(ctx context.Context, sql string, arguments ...any) pgx.Row } func optimizeStorage(ctx context.Context, conn optimizeStorageConn) error { - var targets []string - for _, table := range []string{"node", "edge"} { - var dead, live int64 - if err := conn.QueryRow(ctx, optimizeStorageStatsQuery, table).Scan(&dead, &live); err != nil { - return fmt.Errorf("query dead tuple stats for %s: %w", table, err) - } - - total := dead + live - var deadTupleRatio float64 - if total > 0 { - deadTupleRatio = float64(dead) / float64(total) - } - - slog.InfoContext(ctx, "Queried PostgreSQL table storage statistics", - slog.String("table", table), - slog.Int64("dead_tuples", dead), - slog.Int64("live_tuples", live), - slog.Int64("total_tuples", total), - slog.Float64("dead_tuple_ratio", deadTupleRatio), - slog.Float64("dead_tuple_threshold", deadTupleThreshold), - ) - - if total == 0 { - continue - } - if deadTupleRatio >= deadTupleThreshold { - targets = append(targets, table) - } - } - - if len(targets) == 0 { - return nil - } + targets := []string{"node", "edge"} // Targeting the partitioned parents cascades to every partition. stmt := "VACUUM (ANALYZE) " + strings.Join(targets, ", ") diff --git a/drivers/pg/optimize_test.go b/drivers/pg/optimize_test.go index 8fd0f6c5..6faf9f36 100644 --- a/drivers/pg/optimize_test.go +++ b/drivers/pg/optimize_test.go @@ -2,7 +2,6 @@ package pg import ( "context" - "errors" "testing" "github.com/jackc/pgx/v5" @@ -11,67 +10,15 @@ import ( ) func TestOptimizeStorage(t *testing.T) { - t.Run("skips vacuum when dead tuple ratios are below threshold", func(t *testing.T) { + t.Run("always vacuums node and edge", func(t *testing.T) { ctx := context.Background() conn := newOptimizeStorageMockConn(t) - expectOptimizeStorageStats(conn, "node", 9, 91) - expectOptimizeStorageStats(conn, "edge", 0, 0) - - require.NoError(t, optimizeStorage(ctx, conn)) - require.NoError(t, conn.ExpectationsWereMet()) - }) - - t.Run("vacuums node only", func(t *testing.T) { - ctx := context.Background() - conn := newOptimizeStorageMockConn(t) - - expectOptimizeStorageStats(conn, "node", 10, 90) - expectOptimizeStorageStats(conn, "edge", 9, 91) - expectOptimizeStorageVacuum(conn, "VACUUM (ANALYZE) node") - - require.NoError(t, optimizeStorage(ctx, conn)) - require.NoError(t, conn.ExpectationsWereMet()) - }) - - t.Run("vacuums edge only", func(t *testing.T) { - ctx := context.Background() - conn := newOptimizeStorageMockConn(t) - - expectOptimizeStorageStats(conn, "node", 9, 91) - expectOptimizeStorageStats(conn, "edge", 10, 90) - expectOptimizeStorageVacuum(conn, "VACUUM (ANALYZE) edge") - - require.NoError(t, optimizeStorage(ctx, conn)) - require.NoError(t, conn.ExpectationsWereMet()) - }) - - t.Run("vacuums node and edge", func(t *testing.T) { - ctx := context.Background() - conn := newOptimizeStorageMockConn(t) - - expectOptimizeStorageStats(conn, "node", 10, 90) - expectOptimizeStorageStats(conn, "edge", 10, 90) expectOptimizeStorageVacuum(conn, "VACUUM (ANALYZE) node, edge") require.NoError(t, optimizeStorage(ctx, conn)) require.NoError(t, conn.ExpectationsWereMet()) }) - - t.Run("returns query error", func(t *testing.T) { - ctx := context.Background() - conn := newOptimizeStorageMockConn(t) - expectedErr := errors.New("stats unavailable") - - conn.ExpectQuery(optimizeStorageStatsQuery). - WithArgs("node"). - WillReturnError(expectedErr) - - err := optimizeStorage(ctx, conn) - require.ErrorIs(t, err, expectedErr) - require.ErrorContains(t, err, "query dead tuple stats for node") - require.NoError(t, conn.ExpectationsWereMet()) - }) } func newOptimizeStorageMockConn(t *testing.T) pgxmock.PgxConnIface { @@ -83,12 +30,6 @@ func newOptimizeStorageMockConn(t *testing.T) pgxmock.PgxConnIface { return conn } -func expectOptimizeStorageStats(conn pgxmock.PgxConnIface, table string, dead, live int64) { - conn.ExpectQuery(optimizeStorageStatsQuery). - WithArgs(table). - WillReturnRows(pgxmock.NewRows([]string{"dead", "live"}).AddRow(dead, live)) -} - func expectOptimizeStorageVacuum(conn pgxmock.PgxConnIface, stmt string) { conn.ExpectExec(stmt). WithArgs(pgx.QueryExecModeSimpleProtocol).