我有一个节点js服务器正在运行并配置为自动扩展。此服务器正在访问AWS上的postgresql数据库,我想对其进行版本控制。这里的目标是允许我在代码中推送服务器更新,以便根据需要更改表。到目前为止我所做的是创建了一个BaseDatabase
类,它在服务器启动时连接到数据库(在我启动快速js app之前,所以还没有网络流量)。
连接到数据库后,我使用(在typescript中)检查当前版本:
let transactionComplete = false;
try {
await client.query("BEGIN");
let currentVersion = await client.query("SELECT current_setting('info.version')::int");
if (currentVersion < version) {
try {
await upgradeDatabase(client, currentVersion, version);
} catch (e) {
// Handle error
}
}
await client.query(`SELECT set_config('info.version', ${version}, false)`);
transactionComplete = true;
} catch (e) {
try {
await createDatabase(client);
transactionComplete = true;
} catch (e) {
// Handle error
}
} finally {
if (transactionComplete) {
await client.query('COMMIT');
} else {
await client.query('ROLLBACK');
}
client.release();
}
对于我的所有测试,我可以在不同的端口上运行我的服务器的多个实例,并且我只能创建/更新我的数据库一次。我担心我很幸运。我有一种感觉,即使这只是一次交易,我认为它取决于在SELECT
之前解决的第一个COMMIT
。似乎正确的方法是在我的查询中使用条件并一起执行所有操作。我已经能够创建类似的东西:
DO $$
BEGIN
IF (SELECT current_setting('info.version')::int) < ${version} THEN
RAISE NOTICE 'database requires update';
END IF;
END;
$$;
如果我能在if中获得升级语句,上面的工作和我认为完全是原子的。问题是我在查询info.version
之前不知道我需要哪些语句。在我进行这些查询时,有没有锁定整个数据库?或者可能同步我的所有节点js服务器实例?或者其他我没有想过的东西?
这里的最终目标是,我能够在代码中对数据库进行任何更改,并且当我推送新的服务器版本时,它们将被执行一次。然后,我可以在进行数据库更改之前与我的团队进行代码审查等,并且还可以在本地复制我的prod环境。
答案 0 :(得分:0)
我找到了一个似乎有效的解决方案:
// This is actually a bit more complex to allow for the first instance when
// info.version is not yet defined.
let currentVersion: number = await client.query(
"SELECT current_setting('info.version')");
if (this.version === currentVersion) {
return;
}
let query: string = currentVersion === null
? await this.onCreate(client)
: await this.onUpgrade(client currentVersion, this.version);
query = this.getSqlStatment(currentVersion, this.version, query);
try {
await client.query(query);
} catch (e) {
} finally {
client.release();
}
private getSqlStatement(fromVersion: number, toVersion: number, query: string): string {
return `DO \$\$
DECLARE version integer;
BEGIN
version := current_setting('info.version')::integer;
EXCEPTION
WHEN SQLSTATE '42704' THEN version := NULL;
IF version ${fromVersion ? `= ${fromVersion} : "IS NULL"} THEN
${query}
PERFORM set_config('info.version', '${toVersion}', false);
END IF;
END;
\$\$`;
}