From 7d084d3e7794fea2f8ef01d0de2dcad02140e27b Mon Sep 17 00:00:00 2001 From: Justin Geibel Date: Mon, 20 Jan 2020 00:56:50 -0500 Subject: [PATCH 1/4] Fallback to `READ_ONLY_REPLICA_URL` for db_dump In production, the scheduler was configured to run: ``` ./target/release/enqueue-job dump_db $READ_ONLY_REPLICA ``` instead of: ``` ./target/release/enqueue-job dump_db $READ_ONLY_REPLICA_URL ``` Because of the typo in the variable name, the dump_db job has been running against the primary database. This patch modifies the fallback to ensure the primary database is only used if explicitly provided. --- src/bin/enqueue-job.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/enqueue-job.rs b/src/bin/enqueue-job.rs index 876b9d1c5a9..42a59d41b28 100644 --- a/src/bin/enqueue-job.rs +++ b/src/bin/enqueue-job.rs @@ -13,7 +13,7 @@ fn main() -> Result<(), Error> { match &*job { "update_downloads" => Ok(tasks::update_downloads().enqueue(&conn)?), "dump_db" => { - let database_url = args.next().unwrap_or_else(|| env("DATABASE_URL")); + let database_url = args.next().unwrap_or_else(|| env("READ_ONLY_REPLICA_URL")); let target_name = args .next() .unwrap_or_else(|| String::from("db-dump.tar.gz")); From d8b82354c7f02fe0a6d580c63b5855842722c722 Mon Sep 17 00:00:00 2001 From: Justin Geibel Date: Mon, 20 Jan 2020 19:31:36 -0500 Subject: [PATCH 2/4] Move db_dump filtering logic into main read-only transaction --- src/tasks/dump_db/dump-export.sql.hbs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/tasks/dump_db/dump-export.sql.hbs b/src/tasks/dump_db/dump-export.sql.hbs index 0fcf38cba4d..b80903f4714 100644 --- a/src/tasks/dump_db/dump-export.sql.hbs +++ b/src/tasks/dump_db/dump-export.sql.hbs @@ -1,19 +1,11 @@ -BEGIN; +BEGIN ISOLATION LEVEL SERIALIZABLE READ ONLY DEFERRABLE; {{~#each tables}} {{~#if this.filter}} - CREATE TEMPORARY VIEW "dump_db_{{this.name}}" AS ( + \copy ( SELECT {{this.columns}} FROM "{{this.name}}" WHERE {{this.filter}} - ); -{{~/if}} -{{~/each}} -COMMIT; - -BEGIN ISOLATION LEVEL SERIALIZABLE READ ONLY DEFERRABLE; -{{~#each tables}} -{{~#if this.filter}} - \copy (SELECT * FROM "dump_db_{{this.name}}") TO 'data/{{this.name}}.csv' WITH CSV HEADER + ) TO 'data/{{this.name}}.csv' WITH CSV HEADER {{~else}} \copy "{{this.name}}" ({{this.columns}}) TO 'data/{{this.name}}.csv' WITH CSV HEADER {{~/if}} From a5d69d96d481483a57bd65de2a6cf9318340d499 Mon Sep 17 00:00:00 2001 From: Justin Geibel Date: Mon, 20 Jan 2020 21:35:06 -0500 Subject: [PATCH 3/4] Change dump_db isolation level to "REPEATABLE READ" This addresses the following error: ``` sql_error_code = 0A000 ERROR: cannot use serializable mode in a hot standby sql_error_code = 0A000 HINT: You can use REPEATABLE READ instead. sql_error_code = 0A000 STATEMENT: BEGIN ISOLATION LEVEL SERIALIZABLE READ ONLY DEFERRABLE; ``` Because our application does not rely on any other `SERIALIZABLE` transactions, it should be fine to relax the isoluation level for database dumps to `REPEATABLE READ`. `DEFERRABLE` is dropped because it is only meaningful when combined with `SERIALIZABLE`. --- src/tasks/dump_db/dump-export.sql.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/dump_db/dump-export.sql.hbs b/src/tasks/dump_db/dump-export.sql.hbs index b80903f4714..07fffdb87cf 100644 --- a/src/tasks/dump_db/dump-export.sql.hbs +++ b/src/tasks/dump_db/dump-export.sql.hbs @@ -1,4 +1,4 @@ -BEGIN ISOLATION LEVEL SERIALIZABLE READ ONLY DEFERRABLE; +BEGIN ISOLATION LEVEL REPEATABLE READ, READ ONLY; {{~#each tables}} {{~#if this.filter}} \copy ( From 4ae90e620987acdab05e85bd91e6c3bb07c40ebc Mon Sep 17 00:00:00 2001 From: Justin Geibel Date: Mon, 20 Jan 2020 22:35:13 -0500 Subject: [PATCH 4/4] Ensure `\copy` commands in export.sql are single line --- src/tasks/dump_db/dump-export.sql.hbs | 6 +----- src/tasks/dump_db/gen_scripts.rs | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/tasks/dump_db/dump-export.sql.hbs b/src/tasks/dump_db/dump-export.sql.hbs index 07fffdb87cf..99952df6939 100644 --- a/src/tasks/dump_db/dump-export.sql.hbs +++ b/src/tasks/dump_db/dump-export.sql.hbs @@ -1,11 +1,7 @@ BEGIN ISOLATION LEVEL REPEATABLE READ, READ ONLY; {{~#each tables}} {{~#if this.filter}} - \copy ( - SELECT {{this.columns}} - FROM "{{this.name}}" - WHERE {{this.filter}} - ) TO 'data/{{this.name}}.csv' WITH CSV HEADER + \copy (SELECT {{this.columns}} FROM "{{this.name}}" WHERE {{this.filter}}) TO 'data/{{this.name}}.csv' WITH CSV HEADER {{~else}} \copy "{{this.name}}" ({{this.columns}}) TO 'data/{{this.name}}.csv' WITH CSV HEADER {{~/if}} diff --git a/src/tasks/dump_db/gen_scripts.rs b/src/tasks/dump_db/gen_scripts.rs index e128a0165cd..394b1192280 100644 --- a/src/tasks/dump_db/gen_scripts.rs +++ b/src/tasks/dump_db/gen_scripts.rs @@ -42,7 +42,7 @@ struct TableConfig { #[derive(Debug, Serialize)] struct HandlebarsTableContext<'a> { name: &'a str, - filter: Option<&'a str>, + filter: Option, columns: String, column_defaults: BTreeMap<&'a str, &'a str>, } @@ -59,7 +59,7 @@ impl TableConfig { if columns.is_empty() { None } else { - let filter = self.filter.as_ref().map(String::as_str); + let filter = self.filter.as_ref().map(|s| s.replace('\n', " ")); let column_defaults = self .column_defaults .iter()