Skip to content

Commit

Permalink
migrate(W-14179828): pg-v5: Upgrade pg:settings:auto-explain
Browse files Browse the repository at this point in the history
  • Loading branch information
justinwilaby committed Apr 4, 2024
1 parent 5e66ae7 commit 5683784
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 49 deletions.
38 changes: 38 additions & 0 deletions packages/cli/src/commands/pg/settings/auto-explain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {flags} from '@heroku-cli/command'
import {Args} from '@oclif/core'
import heredoc from 'tsheredoc'
import {PGSettingsCommand, type Setting} from '../../../lib/pg/setter'

// ref: https://www.postgresql.org/docs/current/auto-explain.html
export default class AutoExplain extends PGSettingsCommand {
static topic = 'pg';
static description = heredoc(`
Automatically log execution plans of queries without running EXPLAIN by hand.
The auto_explain module is loaded at session-time so existing connections will not be logged.
Restart your Heroku app and/or restart existing connections for logging to start taking place.'
`)

static flags = {
app: flags.app({required: true}),
remote: flags.remote(),
}

static args = {
database: Args.string(),
value: Args.boolean(),
}

protected settingsName = 'auto_explain'

protected convertValue(val: boolean): boolean {
return val
}

protected explain(setting: Setting): string {
if (setting.value) {
return 'Execution plans of queries will be logged for future connections.'
}

return 'Execution plans of queries will not be logged for future connections.'
}
}
38 changes: 38 additions & 0 deletions packages/cli/src/lib/pg/setter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {APIClient, Command} from '@heroku-cli/command'
import {ux} from '@oclif/core'
import {addonResolver} from '../addons/resolve'
import host from './host'
import {essentialPlan} from './util'
export interface Setting {
value: string
}
export abstract class PGSettingsCommand extends Command {
protected abstract settingsName: string
protected abstract convertValue(val: unknown): unknown
protected abstract explain(setting: Setting): string

public async run(): Promise<void> {
const {flags, args} = await this.parse()
const {app} = flags
const {value, database} = args

const db = await addonResolver(this.heroku, app, database || '')

if (essentialPlan(db)) throw new Error('You can’t perform this operation on Essential-tier databases.')

if (value) {
const {body: settings} = await this.heroku.patch<Record<string, Setting>>(`/postgres/v0/databases/${db.id}/config`, {
hostname: host(),
body: {[this.settingsName]: this.convertValue(value)},
})
const setting = settings[this.settingsName]
ux.log(`${this.settingsName.replace(/_/g, '-')} has been set to ${setting.value} for ${db.name}.`)
ux.log(this.explain(setting))
} else {
const {body: settings} = await this.heroku.get<Record<string, Setting>>(`/postgres/v0/databases/${db.id}/config`, {hostname: host()})
const setting = settings[this.settingsName]
ux.log(`${this.settingsName.replace(/_/g, '-')} is set to ${setting.value} for ${db.name}.`)
ux.log(this.explain(setting))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {expect} from '@oclif/test'
import * as nock from 'nock'
import {stdout} from 'stdout-stderr'
import heredoc from 'tsheredoc'
import runCommand from '../../../../helpers/runCommand'
import Cmd from '../../../../../src/commands/pg/settings/auto-explain'

describe('pg:settings:auto-explain', () => {
let api: nock.Scope
let pg: nock.Scope

beforeEach(() => {
const addon = {
id: 1,
name: 'postgres-1',
app: {name: 'myapp'},
config_vars: ['READONLY_URL', 'DATABASE_URL', 'HEROKU_POSTGRESQL_RED_URL'],
plan: {name: 'heroku-postgresql:standard-0'},
}

api = nock('https://api.heroku.com')
api.post('/actions/addons/resolve', {
app: 'myapp',
addon: 'test-database',
}).reply(200, [addon])

pg = nock('https://api.data.heroku.com')
})

afterEach(() => {
api.done()
pg.done()
})

it('shows settings for auto_explain with value', async () => {
pg.get('/postgres/v0/databases/1/config').reply(200, {auto_explain: {value: 'test_value'}})
await runCommand(Cmd, ['--app', 'myapp', 'test-database'])
expect(stdout.output).to.equal(heredoc(`
auto-explain is set to test_value for postgres-1.
Execution plans of queries will be logged for future connections.
`))
})

it('shows settings for auto_explain with no value', async () => {
pg.get('/postgres/v0/databases/1/config').reply(200, {auto_explain: {value: ''}})
await runCommand(Cmd, ['--app', 'myapp', 'test-database'])
expect(stdout.output).to.equal(heredoc(`
auto-explain is set to for postgres-1.
Execution plans of queries will not be logged for future connections.
`))
})
})
24 changes: 0 additions & 24 deletions packages/pg-v5/commands/settings/auto_explain.js

This file was deleted.

1 change: 0 additions & 1 deletion packages/pg-v5/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ exports.commands = flatten([
require('./commands/repoint'),
require('./commands/reset'),
require('./commands/settings'),
require('./commands/settings/auto_explain'),
require('./commands/settings/auto_explain_log_analyze'),
require('./commands/settings/auto_explain_log_buffers'),
require('./commands/settings/auto_explain_log_min_duration'),
Expand Down
24 changes: 0 additions & 24 deletions packages/pg-v5/test/unit/commands/settings/settings.unit.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,30 +96,6 @@ describe('pg:settings', () => {
.then(() => expect(cli.stdout).to.equal(`${settingsResultName} is set to ${settingResult.value} for postgres-1.\n${settingResult.values[settingResult.value]}\n`))
})

it('shows settings for auto_explain with value', () => {
setupSettingsMockData('auto_explain')
cmd = proxyquire('../../../../commands/settings/auto_explain', {
settings: proxyquire.noCallThru().load('../../../../lib/setter', {
'./fetcher': fetcher,
}),
})
pg.get('/postgres/v0/databases/1/config').reply(200, settingsResult)
return cmd.run({args: {database: 'test-database', value: ''}, flags: {}})
.then(() => expect(cli.stdout).to.equal('auto-explain is set to test_value for postgres-1.\nExecution plans of queries will be logged for future connections.\n'))
})

it('shows settings for auto_explain with no value', () => {
setupSettingsMockData('auto_explain', '')
cmd = proxyquire('../../../../commands/settings/auto_explain', {
settings: proxyquire.noCallThru().load('../../../../lib/setter', {
'./fetcher': fetcher,
}),
})
pg.get('/postgres/v0/databases/1/config').reply(200, settingsResult)
return cmd.run({args: {database: 'test-database', value: ''}, flags: {}})
.then(() => expect(cli.stdout).to.equal('auto-explain is set to for postgres-1.\nExecution plans of queries will not be logged for future connections.\n'))
})

it('shows settings for auto_explain_log_analyze with value', () => {
setupSettingsMockData('auto_explain.log_analyze')
cmd = proxyquire('../../../../commands/settings/auto_explain_log_analyze', {
Expand Down

0 comments on commit 5683784

Please sign in to comment.