Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 24 additions & 7 deletions src/commands/postgres.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ export function alwaysOnLine(group: string, scaleToZero: unknown): string {
return `postgres ${group}: always-on ${scaleToZero === false ? 'on — instance stays warm' : scaleToZero === true ? 'off — scales to zero when idle' : 'unknown — the provider did not report it'}`
}

// Show or set a postgres service's idle mode: scale-to-zero (the default: instance suspends when
// idle, cold-starts on the next connection) or always-on (instance stays warm; idle RAM bills at
// actual usage). Reads GET /database/instance, sets PATCH /database/settings {scaleToZero} —
// Show or set a postgres service's idle mode: always-on (the default for databases since platform
// #666: instance stays warm; idle RAM bills at actual usage) or scale-to-zero (instance suspends
// when idle, cold-starts on the next connection). Reads GET /database/instance, sets PATCH /database/settings {scaleToZero} —
// insta-db-backed postgres only.
export async function dbAlwaysOn(first: string | undefined, second: string | undefined, opts: Opts): Promise<void> {
const { mode, service } = alwaysOnArgs(first, second)
Expand Down Expand Up @@ -70,7 +70,7 @@ export async function settleScaleToZero(
export function alwaysOnSetLine(service: string | undefined, branch: string | undefined, mode: 'on' | 'off', scaleToZero: unknown): string {
const group = service ?? 'default'
if (scaleToZero !== (mode === 'off')) return `postgres ${group}: always-on ${mode} requested — the database has not applied it yet; confirm with \`insta postgres always-on${service ? ` ${service}` : ''}${branch ? ` --branch ${branch}` : ''}\``
return `postgres ${group}: always-on ${mode === 'on' ? 'ENABLED — instance stays warm (no cold starts; idle RAM bills at actual usage)' : 'disabled — scales to zero when idle (default; first connection after idle cold-starts)'}`
return `postgres ${group}: always-on ${mode === 'on' ? 'ENABLED — instance stays warm (no cold starts; idle RAM bills at actual usage; the default for databases)' : 'disabled — scales to zero when idle (first connection after idle cold-starts)'}`
}

// Validated pass-throughs for the provider's quantity strings. The insta-db resize API takes
Expand Down Expand Up @@ -162,9 +162,26 @@ export async function dbLimits(service: string | undefined, opts: Opts & { cpu?:
}
if (handleApproval(res, opts.json)) return
if (opts.json) return printJson(res.body)
const cpu = typeof res.body?.cpuMilli === 'number' ? `${res.body.cpuMilli / 1000} vCPU` : (opts.cpu ?? 'unchanged')
const mem = typeof res.body?.memoryMib === 'number' ? fmtMib(res.body.memoryMib) : (opts.memory ?? 'unchanged')
info(`postgres ${service ?? 'default'}: ceiling set to ${cpu} / ${mem}`)
info(limitsSetLine(service, opts.branch, { cpu: body.cpu as string | undefined, memory: body.memory as string | undefined }, res))
}

// insta-db queues the resize and answers 202 (the platform adds pending: true) with the row from
// BEFORE the patch, so on pending the body's cpuMilli/memoryMib are the old ceiling: print what was
// asked for and point at the read-back, the way alwaysOnSetLine does.
export function limitsSetLine(
service: string | undefined,
branch: string | undefined,
requested: { cpu?: string; memory?: string },
res: { status: number; body: any },
): string {
const group = service ?? 'default'
if (res.status === 202 || res.body?.pending === true) {
const asked = [requested.cpu && `${requested.cpu} vCPU`, requested.memory].filter(Boolean).join(' / ')
return `postgres ${group}: ceiling ${asked} requested, not applied yet — confirm with \`insta postgres limits${service ? ` ${service}` : ''}${branch ? ` --branch ${branch}` : ''}\``
}
const cpu = typeof res.body?.cpuMilli === 'number' ? `${res.body.cpuMilli / 1000} vCPU` : (requested.cpu ?? 'unchanged')
const mem = typeof res.body?.memoryMib === 'number' ? fmtMib(res.body.memoryMib) : (requested.memory ?? 'unchanged')
return `postgres ${group}: ceiling set to ${cpu} / ${mem}`
}

// Bytes → human units, one decimal above KiB. Local because the metrics payload is the only
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ pg.command('volume [service]').description("Show or grow a postgres service's pr
.option('--size <gi>', 'new size in whole Gi, e.g. 10 (must be ≥ the current size)')
.option('--json').option('--branch <branch>', 'branch (default: current)')
.action(guard((service, o) => pgCmd.dbVolume(service, o)))
pg.command('always-on [mode] [service]').description('Show or set a postgres service always-on (mode: on|off; no mode = print the current setting). on = instance stays warm, no cold starts; off = default scale-to-zero (idle instance suspends; first connection cold-starts). insta-db-backed services only')
pg.command('always-on [mode] [service]').description('Show or set a postgres service always-on (mode: on|off; no mode = print the current setting). on = instance stays warm, no cold starts (the default for databases); off = scale-to-zero (idle instance suspends; first connection cold-starts). insta-db-backed services only')
.option('--json').option('--branch <branch>', 'branch (default: current)')
.action(guard((mode, service, o) => pgCmd.dbAlwaysOn(mode, service, o)))
pg.command('private-access [mode] [service]').description('Show or set private network access (mode: on|off; no mode = print public and private access). on = mint DATABASE_PRIVATE_URL beside DATABASE_URL (unchanged) — it resolves only inside InstaCloud compute, not from a laptop or CI; bind it into a compute with `insta secrets bind … --source-name DATABASE_PRIVATE_URL`. off = retract it (refused while public access is closed). insta-db-backed only (gated: service.setAccess)')
Expand Down
22 changes: 21 additions & 1 deletion test/limits.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ describe('parseMemoryMb', () => {
import { parseCpu, fmtMb } from '../src/commands/compute.js'
import { fetchDbInstance } from '../src/commands/postgres.js'
import { ApiError } from '../src/api.js'
import { parseDbCpu, parseDbMemory, fmtMib, alwaysOnArgs, alwaysOnLine, alwaysOnSetLine, settleScaleToZero, restartLine } from '../src/commands/postgres.js'
import { parseDbCpu, parseDbMemory, fmtMib, alwaysOnArgs, alwaysOnLine, alwaysOnSetLine, settleScaleToZero, restartLine, limitsSetLine } from '../src/commands/postgres.js'

describe('parseCpu (compute --cpu override)', () => {
it('accepts exactly the provider grid the help text advertises', () => {
Expand Down Expand Up @@ -164,6 +164,26 @@ describe('postgres always-on set confirmation', () => {
})
})

// insta-db answers a resize with 202 and the row from before the patch: echoing its cpuMilli/memoryMib
// reported the OLD ceiling as the new one (instacloud-cli#369).
describe('postgres limits set confirmation', () => {
it('on 202 prints the requested values as not applied yet, never the pre-change row', () => {
const line = limitsSetLine('db', undefined, { memory: '2Gi' }, { status: 202, body: { cpuMilli: 4000, memoryMib: 4096, pending: true } })

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This fixture cannot verify that HTTP 202 alone triggers the pending message because pending: true independently satisfies the condition. Remove that flag here; the next test already covers the 200/pending case.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At test/limits.test.ts, line 171:

<comment>This fixture cannot verify that HTTP 202 alone triggers the pending message because `pending: true` independently satisfies the condition. Remove that flag here; the next test already covers the 200/pending case.</comment>

<file context>
@@ -164,6 +164,26 @@ describe('postgres always-on set confirmation', () => {
+// reported the OLD ceiling as the new one (instacloud-cli#369).
+describe('postgres limits set confirmation', () => {
+  it('on 202 prints the requested values as not applied yet, never the pre-change row', () => {
+    const line = limitsSetLine('db', undefined, { memory: '2Gi' }, { status: 202, body: { cpuMilli: 4000, memoryMib: 4096, pending: true } })
+    expect(line).toBe('postgres db: ceiling 2Gi requested, not applied yet — confirm with `insta postgres limits db`')
+    expect(line).not.toContain('4 GiB')
</file context>
Suggested change
const line = limitsSetLine('db', undefined, { memory: '2Gi' }, { status: 202, body: { cpuMilli: 4000, memoryMib: 4096, pending: true } })
const line = limitsSetLine('db', undefined, { memory: '2Gi' }, { status: 202, body: { cpuMilli: 4000, memoryMib: 4096 } })

expect(line).toBe('postgres db: ceiling 2Gi requested, not applied yet — confirm with `insta postgres limits db`')
expect(line).not.toContain('4 GiB')
})
it('treats pending: true as not applied even without a 202, and names the branch in the read-back', () => {
expect(limitsSetLine(undefined, 'feat', { cpu: '2', memory: '4Gi' }, { status: 200, body: { cpuMilli: 1000, memoryMib: 1024, pending: true } }))
.toBe('postgres default: ceiling 2 vCPU / 4Gi requested, not applied yet — confirm with `insta postgres limits --branch feat`')
})
it('confirms from the reported row when the change was applied', () => {
expect(limitsSetLine('db', undefined, { memory: '2Gi' }, { status: 200, body: { cpuMilli: 2000, memoryMib: 2048 } }))
.toBe('postgres db: ceiling set to 2 vCPU / 2 GiB')
expect(limitsSetLine('db', undefined, { cpu: '2500m' }, { status: 200, body: {} }))
.toBe('postgres db: ceiling set to 2500m / unchanged')
})
})

describe('postgres restart confirmation', () => {
it('says restarted only when the platform finished it', () => {
expect(restartLine('db', { state: 'running' })).toBe('postgres db: restarted — ALTER SYSTEM settings that need a restart are now in effect')
Expand Down
Loading