feat: 添加强制热表空间的 SQL 构建功能,优化分区创建逻辑
This commit is contained in:
77
bls-onoffline-backend/dist/index.js
vendored
77
bls-onoffline-backend/dist/index.js
vendored
@@ -181,6 +181,69 @@ class PartitionManager {
|
|||||||
today.setHours(0, 0, 0, 0);
|
today.setHours(0, 0, 0, 0);
|
||||||
return normalizedDate.getTime() >= today.getTime();
|
return normalizedDate.getTime() >= today.getTime();
|
||||||
}
|
}
|
||||||
|
escapeSqlLiteral(value) {
|
||||||
|
return String(value).replace(/'/g, "''");
|
||||||
|
}
|
||||||
|
buildForceHotTablespaceSql(schema, partitionName, hotTablespace = "ts_hot") {
|
||||||
|
const schemaLiteral = this.escapeSqlLiteral(schema);
|
||||||
|
const partitionLiteral = this.escapeSqlLiteral(partitionName);
|
||||||
|
const hotLiteral = this.escapeSqlLiteral(hotTablespace);
|
||||||
|
return `
|
||||||
|
DO $$
|
||||||
|
DECLARE
|
||||||
|
v_schema text := '${schemaLiteral}';
|
||||||
|
v_partition text := '${partitionLiteral}';
|
||||||
|
v_hot text := '${hotLiteral}';
|
||||||
|
v_part_oid oid;
|
||||||
|
v_toast_oid oid;
|
||||||
|
r record;
|
||||||
|
BEGIN
|
||||||
|
SELECT c.oid INTO v_part_oid
|
||||||
|
FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace
|
||||||
|
WHERE n.nspname=v_schema AND c.relname=v_partition AND c.relkind='r';
|
||||||
|
|
||||||
|
IF v_part_oid IS NULL THEN
|
||||||
|
RAISE EXCEPTION 'partition %.% not found', v_schema, v_partition;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
EXECUTE format('ALTER TABLE %I.%I SET TABLESPACE %I', v_schema, v_partition, v_hot);
|
||||||
|
|
||||||
|
FOR r IN
|
||||||
|
SELECT idxn.nspname AS index_schema, i.relname AS index_name
|
||||||
|
FROM pg_index x
|
||||||
|
JOIN pg_class t ON t.oid=x.indrelid
|
||||||
|
JOIN pg_namespace nt ON nt.oid=t.relnamespace
|
||||||
|
JOIN pg_class i ON i.oid=x.indexrelid
|
||||||
|
JOIN pg_namespace idxn ON idxn.oid=i.relnamespace
|
||||||
|
LEFT JOIN pg_tablespace ts ON ts.oid=i.reltablespace
|
||||||
|
WHERE nt.nspname=v_schema
|
||||||
|
AND t.relname=v_partition
|
||||||
|
AND COALESCE(ts.spcname,'pg_default')<>v_hot
|
||||||
|
LOOP
|
||||||
|
EXECUTE format('ALTER INDEX %I.%I SET TABLESPACE %I', r.index_schema, r.index_name, v_hot);
|
||||||
|
END LOOP;
|
||||||
|
|
||||||
|
SELECT reltoastrelid INTO v_toast_oid FROM pg_class WHERE oid=v_part_oid;
|
||||||
|
IF v_toast_oid IS NOT NULL AND v_toast_oid<>0 THEN
|
||||||
|
EXECUTE format('ALTER TABLE %s SET TABLESPACE %I', v_toast_oid::regclass, v_hot);
|
||||||
|
|
||||||
|
FOR r IN
|
||||||
|
SELECT idxn.nspname AS index_schema, i.relname AS index_name
|
||||||
|
FROM pg_index x
|
||||||
|
JOIN pg_class i ON i.oid=x.indexrelid
|
||||||
|
JOIN pg_namespace idxn ON idxn.oid=i.relnamespace
|
||||||
|
LEFT JOIN pg_tablespace ts ON ts.oid=i.reltablespace
|
||||||
|
WHERE x.indrelid=v_toast_oid
|
||||||
|
AND COALESCE(ts.spcname,'pg_default')<>v_hot
|
||||||
|
LOOP
|
||||||
|
EXECUTE format('ALTER INDEX %I.%I SET TABLESPACE %I', r.index_schema, r.index_name, v_hot);
|
||||||
|
END LOOP;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
EXECUTE format('ANALYZE %I.%I', v_schema, v_partition);
|
||||||
|
END $$;
|
||||||
|
`;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Calculate the start and end timestamps (milliseconds) for a given date.
|
* Calculate the start and end timestamps (milliseconds) for a given date.
|
||||||
* @param {Date} date - The date to calculate for.
|
* @param {Date} date - The date to calculate for.
|
||||||
@@ -225,13 +288,18 @@ class PartitionManager {
|
|||||||
if (!checkRes.rows[0].exists) {
|
if (!checkRes.rows[0].exists) {
|
||||||
logger.info(`Creating partition ${partitionName} for range [${startMs}, ${endMs})`);
|
logger.info(`Creating partition ${partitionName} for range [${startMs}, ${endMs})`);
|
||||||
console.log(`Creating partition ${partitionName} for range [${startMs}, ${endMs})`);
|
console.log(`Creating partition ${partitionName} for range [${startMs}, ${endMs})`);
|
||||||
const tablespaceClause = this.isCurrentOrFutureDate(targetDate) ? " TABLESPACE ts_hot" : "";
|
const shouldUseHotTablespace = this.isCurrentOrFutureDate(targetDate);
|
||||||
|
const tablespaceClause = shouldUseHotTablespace ? " TABLESPACE ts_hot" : "";
|
||||||
|
const partitionTableName = `${table}_${partitionSuffix}`;
|
||||||
const createSql = `
|
const createSql = `
|
||||||
CREATE TABLE IF NOT EXISTS ${partitionName}
|
CREATE TABLE IF NOT EXISTS ${partitionName}
|
||||||
PARTITION OF ${schema}.${table}
|
PARTITION OF ${schema}.${table}
|
||||||
FOR VALUES FROM (${startMs}) TO (${endMs})${tablespaceClause};
|
FOR VALUES FROM (${startMs}) TO (${endMs})${tablespaceClause};
|
||||||
`;
|
`;
|
||||||
await client.query(createSql);
|
await client.query(createSql);
|
||||||
|
if (shouldUseHotTablespace) {
|
||||||
|
await client.query(this.buildForceHotTablespaceSql(schema, partitionTableName));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.info("Partition check completed.");
|
logger.info("Partition check completed.");
|
||||||
@@ -271,12 +339,17 @@ class PartitionManager {
|
|||||||
const checkRes = await client.query(`SELECT to_regclass($1) as exists;`, [partitionName]);
|
const checkRes = await client.query(`SELECT to_regclass($1) as exists;`, [partitionName]);
|
||||||
if (!checkRes.rows[0].exists) {
|
if (!checkRes.rows[0].exists) {
|
||||||
logger.info(`Creating partition ${partitionName} for range [${startMs}, ${endMs})`);
|
logger.info(`Creating partition ${partitionName} for range [${startMs}, ${endMs})`);
|
||||||
const tablespaceClause = this.isCurrentOrFutureDate(targetDate) ? " TABLESPACE ts_hot" : "";
|
const shouldUseHotTablespace = this.isCurrentOrFutureDate(targetDate);
|
||||||
|
const tablespaceClause = shouldUseHotTablespace ? " TABLESPACE ts_hot" : "";
|
||||||
|
const partitionTableName = `${table}_${partitionSuffix}`;
|
||||||
await client.query(`
|
await client.query(`
|
||||||
CREATE TABLE IF NOT EXISTS ${partitionName}
|
CREATE TABLE IF NOT EXISTS ${partitionName}
|
||||||
PARTITION OF ${schema}.${table}
|
PARTITION OF ${schema}.${table}
|
||||||
FOR VALUES FROM (${startMs}) TO (${endMs})${tablespaceClause};
|
FOR VALUES FROM (${startMs}) TO (${endMs})${tablespaceClause};
|
||||||
`);
|
`);
|
||||||
|
if (shouldUseHotTablespace) {
|
||||||
|
await client.query(this.buildForceHotTablespaceSql(schema, partitionTableName));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -13,6 +13,72 @@ class PartitionManager {
|
|||||||
return normalizedDate.getTime() >= today.getTime();
|
return normalizedDate.getTime() >= today.getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
escapeSqlLiteral(value) {
|
||||||
|
return String(value).replace(/'/g, "''");
|
||||||
|
}
|
||||||
|
|
||||||
|
buildForceHotTablespaceSql(schema, partitionName, hotTablespace = 'ts_hot') {
|
||||||
|
const schemaLiteral = this.escapeSqlLiteral(schema);
|
||||||
|
const partitionLiteral = this.escapeSqlLiteral(partitionName);
|
||||||
|
const hotLiteral = this.escapeSqlLiteral(hotTablespace);
|
||||||
|
|
||||||
|
return `
|
||||||
|
DO $$
|
||||||
|
DECLARE
|
||||||
|
v_schema text := '${schemaLiteral}';
|
||||||
|
v_partition text := '${partitionLiteral}';
|
||||||
|
v_hot text := '${hotLiteral}';
|
||||||
|
v_part_oid oid;
|
||||||
|
v_toast_oid oid;
|
||||||
|
r record;
|
||||||
|
BEGIN
|
||||||
|
SELECT c.oid INTO v_part_oid
|
||||||
|
FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace
|
||||||
|
WHERE n.nspname=v_schema AND c.relname=v_partition AND c.relkind='r';
|
||||||
|
|
||||||
|
IF v_part_oid IS NULL THEN
|
||||||
|
RAISE EXCEPTION 'partition %.% not found', v_schema, v_partition;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
EXECUTE format('ALTER TABLE %I.%I SET TABLESPACE %I', v_schema, v_partition, v_hot);
|
||||||
|
|
||||||
|
FOR r IN
|
||||||
|
SELECT idxn.nspname AS index_schema, i.relname AS index_name
|
||||||
|
FROM pg_index x
|
||||||
|
JOIN pg_class t ON t.oid=x.indrelid
|
||||||
|
JOIN pg_namespace nt ON nt.oid=t.relnamespace
|
||||||
|
JOIN pg_class i ON i.oid=x.indexrelid
|
||||||
|
JOIN pg_namespace idxn ON idxn.oid=i.relnamespace
|
||||||
|
LEFT JOIN pg_tablespace ts ON ts.oid=i.reltablespace
|
||||||
|
WHERE nt.nspname=v_schema
|
||||||
|
AND t.relname=v_partition
|
||||||
|
AND COALESCE(ts.spcname,'pg_default')<>v_hot
|
||||||
|
LOOP
|
||||||
|
EXECUTE format('ALTER INDEX %I.%I SET TABLESPACE %I', r.index_schema, r.index_name, v_hot);
|
||||||
|
END LOOP;
|
||||||
|
|
||||||
|
SELECT reltoastrelid INTO v_toast_oid FROM pg_class WHERE oid=v_part_oid;
|
||||||
|
IF v_toast_oid IS NOT NULL AND v_toast_oid<>0 THEN
|
||||||
|
EXECUTE format('ALTER TABLE %s SET TABLESPACE %I', v_toast_oid::regclass, v_hot);
|
||||||
|
|
||||||
|
FOR r IN
|
||||||
|
SELECT idxn.nspname AS index_schema, i.relname AS index_name
|
||||||
|
FROM pg_index x
|
||||||
|
JOIN pg_class i ON i.oid=x.indexrelid
|
||||||
|
JOIN pg_namespace idxn ON idxn.oid=i.relnamespace
|
||||||
|
LEFT JOIN pg_tablespace ts ON ts.oid=i.reltablespace
|
||||||
|
WHERE x.indrelid=v_toast_oid
|
||||||
|
AND COALESCE(ts.spcname,'pg_default')<>v_hot
|
||||||
|
LOOP
|
||||||
|
EXECUTE format('ALTER INDEX %I.%I SET TABLESPACE %I', r.index_schema, r.index_name, v_hot);
|
||||||
|
END LOOP;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
EXECUTE format('ANALYZE %I.%I', v_schema, v_partition);
|
||||||
|
END $$;
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the start and end timestamps (milliseconds) for a given date.
|
* Calculate the start and end timestamps (milliseconds) for a given date.
|
||||||
* @param {Date} date - The date to calculate for.
|
* @param {Date} date - The date to calculate for.
|
||||||
@@ -66,13 +132,18 @@ class PartitionManager {
|
|||||||
if (!checkRes.rows[0].exists) {
|
if (!checkRes.rows[0].exists) {
|
||||||
logger.info(`Creating partition ${partitionName} for range [${startMs}, ${endMs})`);
|
logger.info(`Creating partition ${partitionName} for range [${startMs}, ${endMs})`);
|
||||||
console.log(`Creating partition ${partitionName} for range [${startMs}, ${endMs})`);
|
console.log(`Creating partition ${partitionName} for range [${startMs}, ${endMs})`);
|
||||||
const tablespaceClause = this.isCurrentOrFutureDate(targetDate) ? ' TABLESPACE ts_hot' : '';
|
const shouldUseHotTablespace = this.isCurrentOrFutureDate(targetDate);
|
||||||
|
const tablespaceClause = shouldUseHotTablespace ? ' TABLESPACE ts_hot' : '';
|
||||||
|
const partitionTableName = `${table}_${partitionSuffix}`;
|
||||||
const createSql = `
|
const createSql = `
|
||||||
CREATE TABLE IF NOT EXISTS ${partitionName}
|
CREATE TABLE IF NOT EXISTS ${partitionName}
|
||||||
PARTITION OF ${schema}.${table}
|
PARTITION OF ${schema}.${table}
|
||||||
FOR VALUES FROM (${startMs}) TO (${endMs})${tablespaceClause};
|
FOR VALUES FROM (${startMs}) TO (${endMs})${tablespaceClause};
|
||||||
`;
|
`;
|
||||||
await client.query(createSql);
|
await client.query(createSql);
|
||||||
|
if (shouldUseHotTablespace) {
|
||||||
|
await client.query(this.buildForceHotTablespaceSql(schema, partitionTableName));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.info('Partition check completed.');
|
logger.info('Partition check completed.');
|
||||||
@@ -119,12 +190,17 @@ class PartitionManager {
|
|||||||
const checkRes = await client.query(`SELECT to_regclass($1) as exists;`, [partitionName]);
|
const checkRes = await client.query(`SELECT to_regclass($1) as exists;`, [partitionName]);
|
||||||
if (!checkRes.rows[0].exists) {
|
if (!checkRes.rows[0].exists) {
|
||||||
logger.info(`Creating partition ${partitionName} for range [${startMs}, ${endMs})`);
|
logger.info(`Creating partition ${partitionName} for range [${startMs}, ${endMs})`);
|
||||||
const tablespaceClause = this.isCurrentOrFutureDate(targetDate) ? ' TABLESPACE ts_hot' : '';
|
const shouldUseHotTablespace = this.isCurrentOrFutureDate(targetDate);
|
||||||
|
const tablespaceClause = shouldUseHotTablespace ? ' TABLESPACE ts_hot' : '';
|
||||||
|
const partitionTableName = `${table}_${partitionSuffix}`;
|
||||||
await client.query(`
|
await client.query(`
|
||||||
CREATE TABLE IF NOT EXISTS ${partitionName}
|
CREATE TABLE IF NOT EXISTS ${partitionName}
|
||||||
PARTITION OF ${schema}.${table}
|
PARTITION OF ${schema}.${table}
|
||||||
FOR VALUES FROM (${startMs}) TO (${endMs})${tablespaceClause};
|
FOR VALUES FROM (${startMs}) TO (${endMs})${tablespaceClause};
|
||||||
`);
|
`);
|
||||||
|
if (shouldUseHotTablespace) {
|
||||||
|
await client.query(this.buildForceHotTablespaceSql(schema, partitionTableName));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
Reference in New Issue
Block a user