Fix v2.19.1 migration schema connection ownership
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m9s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 33s

This commit is contained in:
lzstealth committed 2026-10-24 20:33:20 +01:00
1 parent 9ca372429b
commit 0044cbc1fc
2 files changed
+41 -16

No files matched your search

+8 -11
View File
@@ -830,14 +830,13 @@ const VERSIONED_MIGRATIONS = [
version: '2.19.1',
label: 'v2.19.1 rebuild managed font references',
run: async function (pool) {
const connection = await pool.getConnection();
try {
await connection.beginTransaction();
await connection.query('DELETE FROM c_content_references WHERE reference_type = ?', ['font-family']);
await pool.beginTransaction();
await pool.query('DELETE FROM c_content_references WHERE reference_type = ?', ['font-family']);
let lastSlideId = 0;
while (true) {
const [slides] = await connection.query(
const [slides] = await pool.query(
'SELECT id, content_json FROM c_slides WHERE id > ? AND content_json IS NOT NULL ORDER BY id ASC LIMIT 500',
[lastSlideId]
);
@@ -847,13 +846,13 @@ const VERSIONED_MIGRATIONS = [
references.push.apply(references, collectContentReferences('slide', slide.id, parseJsonSafe(slide.content_json) || {})
.filter(function (reference) { return reference.reference_type === 'font-family'; }));
});
await insertContentReferences(connection, references);
await insertContentReferences(pool, references);
lastSlideId = Number(slides[slides.length - 1].id);
}
let lastRegionId = 0;
while (true) {
const [regions] = await connection.query(
const [regions] = await pool.query(
'SELECT id, template_id, region_key, default_content_json FROM c_template_regions WHERE id > ? AND default_content_json IS NOT NULL ORDER BY id ASC LIMIT 500',
[lastRegionId]
);
@@ -865,16 +864,14 @@ const VERSIONED_MIGRATIONS = [
references.push.apply(references, collectContentReferences('template', region.template_id, regionContent)
.filter(function (reference) { return reference.reference_type === 'font-family'; }));
});
await insertContentReferences(connection, references);
await insertContentReferences(pool, references);
lastRegionId = Number(regions[regions.length - 1].id);
}
await connection.commit();
await pool.commit();
} catch (error) {
await connection.rollback();
await pool.rollback();
throw error;
} finally {
connection.release();
}
}
}
+33 -5
View File
@@ -9,11 +9,9 @@ function createPool(responses) {
const queries = [];
return {
queries,
async getConnection() { return this; },
async beginTransaction() { queries.push(['START TRANSACTION']); },
async commit() { queries.push(['COMMIT']); },
async rollback() { queries.push(['ROLLBACK']); },
release() { queries.push(['RELEASE CONNECTION']); },
async query(sql, params) {
queries.push([sql, params]);
const text = String(sql);
@@ -244,11 +242,11 @@ test('2.19.1 migration rebuilds only managed font references for all content in
assert.ok(pool.queries.some(([sql, params]) => sql.startsWith('SELECT id, content_json') && params[0] === 118));
assert.ok(pool.queries.some(([sql, params]) => sql.startsWith('SELECT id, template_id, region_key') && params[0] === 900));
assert.ok(!pool.queries.some(([sql]) => /^(UPDATE|ALTER|TRUNCATE)/.test(sql)));
assert.deepEqual(pool.queries.slice(-2), [['COMMIT'], ['RELEASE CONNECTION']]);
assert.deepEqual(pool.queries.slice(-1), [['COMMIT']]);
assert.deepEqual(await getPendingMigrations(pool, { currentVersion: '2.19.1' }), []);
});
test('2.19.1 font repair rolls back and releases its connection on failure', async () => {
test('2.19.1 font repair rolls back without releasing the caller-owned connection on failure', async () => {
const pool = createPool([
{ match: sql => sql.includes('information_schema.COLUMNS'), result: [[{ column_count: 1 }]] },
{ match: sql => sql.includes('information_schema.TABLES'), result: [[{ table_count: 0 }]] }
@@ -261,6 +259,36 @@ test('2.19.1 font repair rolls back and releases its connection on failure', asy
};
await assert.rejects(migration.run(pool), /read failed/);
assert.deepEqual(pool.queries.slice(-2), [['ROLLBACK'], ['RELEASE CONNECTION']]);
assert.deepEqual(pool.queries.slice(-1), [['ROLLBACK']]);
assert.ok(!pool.queries.some(([sql]) => sql === 'COMMIT'));
});
test('schema startup runs the 2.19.1 repair on its existing locked connection', async () => {
const { ensureSchema } = require('../src/db');
const connection = createPool([
{ match: sql => sql.includes('SELECT GET_LOCK'), result: [[{ lock_acquired: 1 }]] },
{ match: (sql, params) => sql.includes('information_schema.TABLES') && params[0] === 'o_app_state', result: [[{ table_count: 1 }]] },
{ match: sql => sql.startsWith('SELECT state_value'), result: [[{ state_value: '2.19.0' }]] },
{ match: sql => sql.includes('information_schema.COLUMNS'), result: [[{ column_count: 1 }]] },
{ match: sql => sql.includes('information_schema.TABLES'), result: [[{ table_count: 0 }]] }
]);
connection.release = function () { connection.queries.push(['RELEASE CONNECTION']); };
let acquisitions = 0;
await ensureSchema({
async getConnection() {
acquisitions += 1;
return connection;
}
});
assert.equal(acquisitions, 1);
const commitIndex = connection.queries.findIndex(([sql]) => sql === 'COMMIT');
const versionIndex = connection.queries.findIndex(([sql, params]) => sql.startsWith('UPDATE o_app_state') && params[0] === '2.19.1');
assert.ok(commitIndex > 0);
assert.ok(versionIndex > commitIndex);
assert.deepEqual(connection.queries.slice(-2), [
['SELECT RELEASE_LOCK(?)', ['pulse_signage_schema_lock']],
['RELEASE CONNECTION']
]);
});