pipework 0.293.0 → 0.294.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/REFERENCE.md +9 -0
- package/dist/cli/commands/migrate.d.ts.map +1 -1
- package/dist/cli/commands/migrate.js +17 -2
- package/dist/cli/commands/migrate.js.map +1 -1
- package/dist/data/migrate/apply.d.ts +8 -0
- package/dist/data/migrate/apply.d.ts.map +1 -1
- package/dist/data/migrate/apply.js +43 -10
- package/dist/data/migrate/apply.js.map +1 -1
- package/dist/data/migrate/index.d.ts +1 -1
- package/dist/data/migrate/index.d.ts.map +1 -1
- package/dist/data/migrate/index.js +1 -1
- package/dist/data/migrate/index.js.map +1 -1
- package/dist/data/migrate/invalid-indexes.d.ts +51 -0
- package/dist/data/migrate/invalid-indexes.d.ts.map +1 -0
- package/dist/data/migrate/invalid-indexes.js +219 -0
- package/dist/data/migrate/invalid-indexes.js.map +1 -0
- package/dist/data/migrate/run.d.ts +23 -1
- package/dist/data/migrate/run.d.ts.map +1 -1
- package/dist/data/migrate/run.js +38 -0
- package/dist/data/migrate/run.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/migrate.ts +17 -2
- package/src/data/migrate/apply.ts +53 -9
- package/src/data/migrate/index.ts +1 -1
- package/src/data/migrate/invalid-indexes.ts +296 -0
- package/src/data/migrate/run.ts +51 -1
package/src/data/migrate/run.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import postgres from 'postgres'
|
|
1
2
|
import { sql } from '../query/index.js'
|
|
2
3
|
import type { Manifold } from '../../core/pipework.js'
|
|
3
4
|
import type { ResolvedDatabase } from '../../core/config/index.js'
|
|
@@ -6,6 +7,7 @@ import { OWNER_LABEL_MIGRATE } from '../db/owner-labels.js'
|
|
|
6
7
|
import { checkMigrations, type MigrationCheckResult } from './check.js'
|
|
7
8
|
import { readAppliedState } from './applied.js'
|
|
8
9
|
import { applyMigrations, type MigrationNotice } from './apply.js'
|
|
10
|
+
import { repairInvalidIndexes, InvalidIndexRepairError, type InvalidIndex } from './invalid-indexes.js'
|
|
9
11
|
import { provisionFrameworkTables, type FrameworkTablesResult } from './provision-framework.js'
|
|
10
12
|
|
|
11
13
|
// Deliberately empty of hazard switches. Authorization for a hazardous
|
|
@@ -24,8 +26,16 @@ export interface MigrateResult {
|
|
|
24
26
|
safetyCheck: MigrationCheckResult
|
|
25
27
|
/** No-transaction files picked up half applied and finished. Issue #378. */
|
|
26
28
|
resumed: ResumedMigration[]
|
|
27
|
-
/** INVALID indexes a cut CREATE INDEX CONCURRENTLY left, dropped before the
|
|
29
|
+
/** INVALID indexes a cut CREATE INDEX CONCURRENTLY left, dropped before the statement that builds them reran. Issues #1003, #1005. */
|
|
28
30
|
droppedInvalidIndexes: DroppedInvalidIndex[]
|
|
31
|
+
/**
|
|
32
|
+
* Every INVALID index this run met, with its case, what was done or must be
|
|
33
|
+
* done, and the outcome: (a) an owned index rebuilt, (b) a leftover of an owned
|
|
34
|
+
* index dropped, (c) anything else, untouched. Issue #1005.
|
|
35
|
+
*/
|
|
36
|
+
invalidIndexes: InvalidIndex[]
|
|
37
|
+
/** Set when the catalog read for INVALID indexes failed, so none were checked. Issue #1005. */
|
|
38
|
+
invalidIndexReadError: string | null
|
|
29
39
|
/** Notices and warnings raised by migration statements, in arrival order. Issue #970. */
|
|
30
40
|
notices: MigrateNotice[]
|
|
31
41
|
/** Trace/audit tables, columns and day partitions this run provisioned. */
|
|
@@ -54,6 +64,8 @@ export interface MigrateNotice extends MigrationNotice {
|
|
|
54
64
|
statements: number
|
|
55
65
|
}
|
|
56
66
|
|
|
67
|
+
export { InvalidIndexRepairError, type InvalidIndex }
|
|
68
|
+
|
|
57
69
|
export class UnsafeMigrationError extends Error {
|
|
58
70
|
override readonly name = 'UnsafeMigrationError'
|
|
59
71
|
constructor(
|
|
@@ -76,7 +88,34 @@ export async function migrateAll(instance: Manifold, options: MigrateOptions = {
|
|
|
76
88
|
return results
|
|
77
89
|
}
|
|
78
90
|
|
|
91
|
+
/** One key per database: advisory locks are scoped to the database they are taken in. */
|
|
92
|
+
const MIGRATE_LOCK_KEY = `hashtextextended('pipework.migrate', 0)`
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The connection that holds the run lock. It must live as long as the run: the
|
|
96
|
+
* default `max_lifetime` would close it mid-run and release the lock, so both
|
|
97
|
+
* timeouts are off, as in src/test/setup.ts.
|
|
98
|
+
*/
|
|
99
|
+
export function openRunLock(url: string): ReturnType<typeof postgres> {
|
|
100
|
+
return postgres(url, { max: 1, idle_timeout: 0, max_lifetime: 0, onnotice: () => {} })
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Runs one database's migrations and index repairs under a session advisory
|
|
105
|
+
* lock held for the whole run. A second run at once waits for it, then finds
|
|
106
|
+
* nothing to apply. The lock dies with its connection. Issue #1005.
|
|
107
|
+
*/
|
|
79
108
|
export async function migrateOne(instance: Manifold, dbConfig: ResolvedDatabase, options: MigrateOptions = {}): Promise<MigrateResult> {
|
|
109
|
+
const lock = openRunLock(dbConfig.url)
|
|
110
|
+
try {
|
|
111
|
+
await lock.unsafe(`SELECT pg_advisory_lock(${MIGRATE_LOCK_KEY})`)
|
|
112
|
+
return await migrateLocked(instance, dbConfig, options)
|
|
113
|
+
} finally {
|
|
114
|
+
await lock.end()
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function migrateLocked(instance: Manifold, dbConfig: ResolvedDatabase, options: MigrateOptions): Promise<MigrateResult> {
|
|
80
119
|
const conn = instance.pool.getOrCreate(dbConfig, 'owner', OWNER_LABEL_MIGRATE)
|
|
81
120
|
|
|
82
121
|
// Scope the safety gate to pending migrations only — hazards in already-applied
|
|
@@ -124,6 +163,15 @@ export async function migrateOne(instance: Manifold, dbConfig: ResolvedDatabase,
|
|
|
124
163
|
},
|
|
125
164
|
})
|
|
126
165
|
|
|
166
|
+
// After a run that finished without error only; a failing migration throws
|
|
167
|
+
// above and its rerun resumes and checks then. Throws when an owned index
|
|
168
|
+
// cannot be made valid.
|
|
169
|
+
const repaired = await repairInvalidIndexes({
|
|
170
|
+
migrationsFolder: dbConfig.migrations!,
|
|
171
|
+
migrationsTable: dbConfig.migrationTable,
|
|
172
|
+
connectionUrl: dbConfig.url,
|
|
173
|
+
})
|
|
174
|
+
|
|
127
175
|
// Trace and audit storage rides the default database; their partitioned
|
|
128
176
|
// tables are kept out of generated migrations (#313), so migrate provisions them.
|
|
129
177
|
const frameworkTables = await provisionFrameworkTables(
|
|
@@ -146,6 +194,8 @@ export async function migrateOne(instance: Manifold, dbConfig: ResolvedDatabase,
|
|
|
146
194
|
safetyCheck,
|
|
147
195
|
resumed,
|
|
148
196
|
droppedInvalidIndexes,
|
|
197
|
+
invalidIndexes: repaired.indexes,
|
|
198
|
+
invalidIndexReadError: repaired.readError,
|
|
149
199
|
notices,
|
|
150
200
|
frameworkTables,
|
|
151
201
|
}
|