Skip to content

Commit ec16d98

Browse files
authored
Add delta base TPS with fix for metrics collection (#157)
1 parent e2083ca commit ec16d98

File tree

2 files changed

+46
-5
lines changed

2 files changed

+46
-5
lines changed

internal/pgmetrics/collector.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,19 +193,22 @@ func (d *defaultStatsCollector) QueryStats(ctx context.Context, db *sql.DB) (Pos
193193
COALESCE(SUM(xact_commit), 0) AS xact_commit,
194194
COALESCE(SUM(xact_rollback), 0) AS xact_rollback,
195195
COALESCE(SUM(blks_read), 0) AS blks_read,
196-
COALESCE(MIN(stats_reset), now()) AS stats_reset
197-
FROM pg_stat_database
198-
WHERE stats_reset IS NOT NULL;
196+
MIN(stats_reset) AS stats_reset
197+
FROM pg_stat_database;
199198
`
200199
var stats PostgresStats
200+
var statsReset sql.NullTime
201201
err := db.QueryRowContext(ctx, query).Scan(
202202
&stats.XactCommit,
203203
&stats.XactRollback,
204204
&stats.BlksRead,
205-
&stats.StatsReset,
205+
&statsReset,
206206
)
207207
if err != nil {
208208
return PostgresStats{}, fmt.Errorf("query stats: %w", err)
209209
}
210+
if statsReset.Valid {
211+
stats.StatsReset = statsReset.Time
212+
}
210213
return stats, nil
211214
}

test/integration/pgmetrics_test.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func setupPostgresContainer(t *testing.T) (*postgres.PostgresContainer, credenti
3232
ctx := context.Background()
3333

3434
pgContainer, err := postgres.Run(ctx,
35-
"postgres:16-alpine",
35+
"postgres:18-alpine",
3636
postgres.WithDatabase(testDatabase),
3737
postgres.WithUsername(testUser),
3838
postgres.WithPassword(testPassword),
@@ -165,6 +165,44 @@ func TestCollector_Collect_CacheHitRatio(t *testing.T) {
165165
"cache hit ratio should be greater than 0 after queries")
166166
}
167167

168+
func TestCollector_Collect_DeltaBasedTPS(t *testing.T) {
169+
_, cred := setupPostgresContainer(t)
170+
171+
// Generate some initial activity to ensure stats_reset is populated
172+
connStr := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
173+
cred.Host, cred.Port, cred.Username, *cred.Password, cred.Database)
174+
db, err := sql.Open("postgres", connStr)
175+
require.NoError(t, err)
176+
defer db.Close()
177+
178+
ctx := context.Background()
179+
_, err = db.ExecContext(ctx, "SELECT 1")
180+
require.NoError(t, err)
181+
182+
collector := pgmetrics.New()
183+
184+
// First collection establishes baseline, returns 0 for rate metrics
185+
metrics1, err := collector.Collect(cred)
186+
require.NoError(t, err)
187+
assert.Equal(t, 0.0, metrics1.TransactionsPerSecond, "first collection should return 0 TPS")
188+
assert.Equal(t, 0.0, metrics1.CommittedTxPerSecond, "first collection should return 0 committed TPS")
189+
190+
// Generate transactions between collections
191+
for range 100 {
192+
_, err = db.ExecContext(ctx, "SELECT 1")
193+
require.NoError(t, err)
194+
}
195+
196+
// PostgreSQL stats flush at minimum 1-second intervals
197+
time.Sleep(1 * time.Second)
198+
199+
// Second collection calculates delta-based TPS
200+
metrics2, err := collector.Collect(cred)
201+
require.NoError(t, err)
202+
assert.Greater(t, metrics2.TransactionsPerSecond, 0.0, "second collection should have TPS > 0")
203+
assert.Greater(t, metrics2.CommittedTxPerSecond, 0.0, "second collection should have committed TPS > 0")
204+
}
205+
168206
func BenchmarkCollector_Collect(b *testing.B) {
169207
_, cred := setupPostgresContainer(&testing.T{})
170208
setupTestData(&testing.T{}, cred)

0 commit comments

Comments
 (0)