在节点js服务器

时间:2017-09-24 14:30:51

标签: node.js postgresql

我有一个节点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环境。

1 个答案:

答案 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;
\$\$`;
}