1 package metrics
2
3 import (
4 "context"
5 _ "embed"
6 "fmt"
7
8 "github.com/cybertec-postgresql/pgwatch/v3/internal/db"
9 "github.com/cybertec-postgresql/pgwatch/v3/internal/log"
10 migrator "github.com/cybertec-postgresql/pgx-migrator"
11 "github.com/jackc/pgx/v5"
12 )
13
14
15 var sqlConfigSchema string
16
17 var initSchema = func(ctx context.Context, conn db.PgxIface) (err error) {
18 var exists bool
19 if exists, err = db.DoesSchemaExist(ctx, conn, "pgwatch"); err != nil || exists {
20 return err
21 }
22 tx, err := conn.Begin(ctx)
23 if err != nil {
24 return err
25 }
26 defer func() { _ = tx.Rollback(ctx) }()
27 if _, err := tx.Exec(ctx, sqlConfigSchema); err != nil {
28 return err
29 }
30 if err := writeMetricsToPostgres(ctx, tx, GetDefaultMetrics()); err != nil {
31 return err
32 }
33 if err := tx.Commit(ctx); err != nil {
34 return err
35 }
36 return nil
37 }
38
39 var initMigrator = func(dmrw *dbMetricReaderWriter) (*migrator.Migrator, error) {
40 return migrator.New(
41 migrator.TableName("pgwatch.migration"),
42 migrator.SetNotice(func(s string) {
43 log.GetLogger(dmrw.ctx).Info(s)
44 }),
45 migrations(),
46 )
47 }
48
49
50 func (dmrw *dbMetricReaderWriter) Migrate() error {
51 m, err := initMigrator(dmrw)
52 if err != nil {
53 return fmt.Errorf("cannot initialize migration: %w", err)
54 }
55 return m.Migrate(dmrw.ctx, dmrw.configDb)
56 }
57
58
59 func (dmrw *dbMetricReaderWriter) NeedsMigration() (bool, error) {
60 m, err := initMigrator(dmrw)
61 if err != nil {
62 return false, err
63 }
64 return m.NeedUpgrade(dmrw.ctx, dmrw.configDb)
65 }
66
67
68 var migrations func() migrator.Option = func() migrator.Option {
69 return migrator.Migrations(
70 &migrator.Migration{
71 Name: "00179 Apply metrics migrations for v3",
72 Func: func(context.Context, pgx.Tx) error {
73
74 return nil
75 },
76 },
77
78 &migrator.Migration{
79 Name: "00824 Refactor recommendations",
80 Func: func(ctx context.Context, tx pgx.Tx) error {
81 _, err := tx.Exec(ctx, `
82 -- 1. Update all reco_ metrics to use metric_storage_name = 'recommendations'
83 UPDATE pgwatch.metric
84 SET storage_name = 'recommendations'
85 WHERE name LIKE 'reco_%' AND COALESCE(storage_name, '') = '';
86
87 -- 2. Remove the placeholder 'recommendations' metric if it exists
88 DELETE FROM pgwatch.metric WHERE name = 'recommendations';
89
90 -- 3. Update 'exhaustive' and 'full' presets to replace 'recommendations' with individual reco_ metrics
91 UPDATE pgwatch.preset
92 SET metrics = metrics - 'recommendations' || $reco_metrics${
93 "reco_add_index": 43200,
94 "reco_default_public_schema": 50400,
95 "reco_disabled_triggers": 57600,
96 "reco_drop_index": 64800,
97 "reco_nested_views": 72000,
98 "reco_partial_index_candidates": 79200,
99 "reco_sprocs_wo_search_path": 86400,
100 "reco_superusers": 93600
101 }$reco_metrics$::jsonb
102 WHERE name IN ('exhaustive', 'full') AND metrics ? 'recommendations';
103
104 -- 4. Insert new 'recommendations' preset if it doesn't exist
105 INSERT INTO pgwatch.preset (name, description, metrics)
106 VALUES ('recommendations', 'performance and security recommendations',
107 $reco_metrics${
108 "reco_add_index": 43200,
109 "reco_default_public_schema": 50400,
110 "reco_disabled_triggers": 57600,
111 "reco_drop_index": 64800,
112 "reco_nested_views": 72000,
113 "reco_partial_index_candidates": 79200,
114 "reco_sprocs_wo_search_path": 86400,
115 "reco_superusers": 93600
116 }$reco_metrics$::jsonb)
117 ON CONFLICT (name) DO NOTHING;
118
119 -- 5. Update source configs to replace 'recommendations' with individual reco_ metrics
120 UPDATE pgwatch.source
121 SET config = config - 'recommendations' ||
122 $reco_metrics${
123 "reco_add_index": 43200,
124 "reco_default_public_schema": 50400,
125 "reco_disabled_triggers": 57600,
126 "reco_drop_index": 64800,
127 "reco_nested_views": 72000,
128 "reco_partial_index_candidates": 79200,
129 "reco_sprocs_wo_search_path": 86400,
130 "reco_superusers": 93600
131 }$reco_metrics$::jsonb
132 WHERE config ? 'recommendations';
133
134 -- 6. Update source standby configs to replace 'recommendations' with individual reco_ metrics
135 UPDATE pgwatch.source
136 SET config_standby = config_standby - 'recommendations' ||
137 $reco_metrics${
138 "reco_add_index": 43200,
139 "reco_default_public_schema": 50400,
140 "reco_disabled_triggers": 57600,
141 "reco_drop_index": 64800,
142 "reco_nested_views": 72000,
143 "reco_partial_index_candidates": 79200,
144 "reco_sprocs_wo_search_path": 86400,
145 "reco_superusers": 93600
146 }$reco_metrics$::jsonb
147 WHERE config_standby ? 'recommendations';
148 `)
149 return err
150 },
151 },
152
153
154
155
156
157
158
159
160
161
162 )
163 }
164