Add ancestor id columns to plugins and plugin_cells

With temp backfill queries. Second stage will to make the columns nullable and add foreign keys.

I'm hoping that this will improve query performance.
This commit is contained in:
Tyler Hallada 2022-01-24 21:13:07 -05:00
parent 4a8e3cad6c
commit e5936683fa
6 changed files with 67 additions and 14 deletions

View File

@ -0,0 +1,4 @@
/* TODO: make these non-nullable and add foreign keys */
ALTER TABLE "plugins" ADD COLUMN "mod_id" INTEGER;
ALTER TABLE "plugin_cells" ADD COLUMN "file_id" INTEGER;
ALTER TABLE "plugin_cells" ADD COLUMN "mod_id" INTEGER;

View File

@ -215,6 +215,35 @@ pub async fn main() -> Result<()> {
.connect(&env::var("DATABASE_URL")?) .connect(&env::var("DATABASE_URL")?)
.await?; .await?;
// Temporary backfill
sqlx::query!(
r#"UPDATE plugins
SET mod_id = files.mod_id
FROM files
WHERE
files.id = plugins.file_id AND
plugins.mod_id IS NULL
"#,
)
.execute(&pool)
.await?;
sqlx::query!(
r#"UPDATE plugin_cells
SET
file_id = plugins.file_id,
mod_id = files.mod_id
FROM plugins
JOIN files ON plugins.file_id = files.id
WHERE
plugins.id = plugin_cells.plugin_id AND
plugin_cells.file_id IS NULL AND
plugin_cells.mod_id IS NULL
"#,
)
.execute(&pool)
.await?;
return Ok(());
let args: Args = argh::from_env(); let args: Args = argh::from_env();
if let Some(dump_edits) = args.dump_edits { if let Some(dump_edits) = args.dump_edits {

View File

@ -130,8 +130,8 @@ pub async fn count_mod_edits(
FROM cells FROM cells
JOIN plugin_cells on cells.id = cell_id JOIN plugin_cells on cells.id = cell_id
JOIN plugins ON plugins.id = plugin_id JOIN plugins ON plugins.id = plugin_id
JOIN files ON files.id = file_id JOIN files ON files.id = plugins.file_id
JOIN mods ON mods.id = mod_id JOIN mods ON mods.id = files.mod_id
WHERE master = $1 AND world_id = $2 AND x = $3 and y = $4", WHERE master = $1 AND world_id = $2 AND x = $3 and y = $4",
master, master,
world_id, world_id,
@ -166,8 +166,8 @@ pub async fn get_cell_data(
FROM cells FROM cells
JOIN plugin_cells on cells.id = cell_id JOIN plugin_cells on cells.id = cell_id
JOIN plugins ON plugins.id = plugin_id JOIN plugins ON plugins.id = plugin_id
JOIN files ON files.id = file_id JOIN files ON files.id = plugins.file_id
JOIN mods ON mods.id = mod_id JOIN mods ON mods.id = files.mod_id
WHERE master = $1 AND world_id = $2 AND x = $3 and y = $4 WHERE master = $1 AND world_id = $2 AND x = $3 and y = $4
GROUP BY cells.x, cells.y, cells.is_persistent, cells.form_id"#, GROUP BY cells.x, cells.y, cells.is_persistent, cells.form_id"#,
master, master,

View File

@ -10,6 +10,7 @@ pub struct Plugin {
pub name: String, pub name: String,
pub hash: i64, pub hash: i64,
pub file_id: i32, pub file_id: i32,
pub mod_id: Option<i32>,
pub version: f64, pub version: f64,
pub size: i64, pub size: i64,
pub author: Option<String>, pub author: Option<String>,
@ -26,6 +27,7 @@ pub struct UnsavedPlugin<'a> {
pub name: &'a str, pub name: &'a str,
pub hash: i64, pub hash: i64,
pub file_id: i32, pub file_id: i32,
pub mod_id: Option<i32>,
pub version: f64, pub version: f64,
pub size: i64, pub size: i64,
pub author: Option<&'a str>, pub author: Option<&'a str>,
@ -43,16 +45,17 @@ pub async fn insert<'a>(
// sqlx doesn't understand slices of &str with the query_as! macro: https://github.com/launchbadge/sqlx/issues/280 // sqlx doesn't understand slices of &str with the query_as! macro: https://github.com/launchbadge/sqlx/issues/280
sqlx::query_as( sqlx::query_as(
r#"INSERT INTO plugins r#"INSERT INTO plugins
(name, hash, file_id, version, size, author, description, masters, file_name, file_path, created_at, updated_at) (name, hash, file_id, mod_id, version, size, author, description, masters, file_name, file_path, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, now(), now()) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, now(), now())
ON CONFLICT (file_id, file_path) DO UPDATE ON CONFLICT (file_id, file_path) DO UPDATE
SET (name, hash, version, author, description, masters, file_name, updated_at) = SET (name, hash, mod_id, version, author, description, masters, file_name, updated_at) =
(EXCLUDED.name, EXCLUDED.hash, EXCLUDED.version, EXCLUDED.author, EXCLUDED.description, EXCLUDED.masters, EXCLUDED.file_name, now()) (EXCLUDED.name, EXCLUDED.hash, EXCLUDED.mod_id, EXCLUDED.version, EXCLUDED.author, EXCLUDED.description, EXCLUDED.masters, EXCLUDED.file_name, now())
RETURNING *"#, RETURNING *"#,
) )
.bind(unsaved_plugin.name) .bind(unsaved_plugin.name)
.bind(unsaved_plugin.hash) .bind(unsaved_plugin.hash)
.bind(unsaved_plugin.file_id) .bind(unsaved_plugin.file_id)
.bind(unsaved_plugin.mod_id)
.bind(unsaved_plugin.version) .bind(unsaved_plugin.version)
.bind(unsaved_plugin.size) .bind(unsaved_plugin.size)
.bind(unsaved_plugin.author) .bind(unsaved_plugin.author)

View File

@ -11,6 +11,8 @@ pub struct PluginCell {
pub id: i32, pub id: i32,
pub plugin_id: i32, pub plugin_id: i32,
pub cell_id: i32, pub cell_id: i32,
pub file_id: Option<i32>,
pub mod_id: Option<i32>,
pub editor_id: Option<String>, pub editor_id: Option<String>,
pub updated_at: NaiveDateTime, pub updated_at: NaiveDateTime,
pub created_at: NaiveDateTime, pub created_at: NaiveDateTime,
@ -20,6 +22,8 @@ pub struct PluginCell {
pub struct UnsavedPluginCell<'a> { pub struct UnsavedPluginCell<'a> {
pub plugin_id: i32, pub plugin_id: i32,
pub cell_id: i32, pub cell_id: i32,
pub file_id: Option<i32>,
pub mod_id: Option<i32>,
pub editor_id: Option<&'a str>, pub editor_id: Option<&'a str>,
} }
@ -28,18 +32,22 @@ pub async fn insert(
pool: &sqlx::Pool<sqlx::Postgres>, pool: &sqlx::Pool<sqlx::Postgres>,
plugin_id: i32, plugin_id: i32,
cell_id: i32, cell_id: i32,
file_id: Option<i32>,
mod_id: Option<i32>,
editor_id: Option<String>, editor_id: Option<String>,
) -> Result<PluginCell> { ) -> Result<PluginCell> {
sqlx::query_as!( sqlx::query_as!(
PluginCell, PluginCell,
"INSERT INTO plugin_cells "INSERT INTO plugin_cells
(plugin_id, cell_id, editor_id, created_at, updated_at) (plugin_id, cell_id, file_id, mod_id, editor_id, created_at, updated_at)
VALUES ($1, $2, $3, now(), now()) VALUES ($1, $2, $3, $4, $5, now(), now())
ON CONFLICT (plugin_id, cell_id) DO UPDATE ON CONFLICT (plugin_id, cell_id) DO UPDATE
SET (editor_id, updated_at) = (EXCLUDED.editor_id, now()) SET (editor_id, updated_at) = (EXCLUDED.editor_id, now())
RETURNING *", RETURNING *",
plugin_id, plugin_id,
cell_id, cell_id,
file_id,
mod_id,
editor_id, editor_id,
) )
.fetch_one(pool) .fetch_one(pool)
@ -56,23 +64,29 @@ pub async fn batched_insert<'a>(
for batch in plugin_cells.chunks(BATCH_SIZE) { for batch in plugin_cells.chunks(BATCH_SIZE) {
let mut plugin_ids: Vec<i32> = vec![]; let mut plugin_ids: Vec<i32> = vec![];
let mut cell_ids: Vec<i32> = vec![]; let mut cell_ids: Vec<i32> = vec![];
let mut file_ids: Vec<Option<i32>> = vec![];
let mut mod_ids: Vec<Option<i32>> = vec![];
let mut editor_ids: Vec<Option<&str>> = vec![]; let mut editor_ids: Vec<Option<&str>> = vec![];
batch.iter().for_each(|unsaved_plugin_cell| { batch.iter().for_each(|unsaved_plugin_cell| {
plugin_ids.push(unsaved_plugin_cell.plugin_id); plugin_ids.push(unsaved_plugin_cell.plugin_id);
cell_ids.push(unsaved_plugin_cell.cell_id); cell_ids.push(unsaved_plugin_cell.cell_id);
file_ids.push(unsaved_plugin_cell.file_id);
mod_ids.push(unsaved_plugin_cell.mod_id);
editor_ids.push(unsaved_plugin_cell.editor_id); editor_ids.push(unsaved_plugin_cell.editor_id);
}); });
saved_plugin_cells.append( saved_plugin_cells.append(
// sqlx doesn't understand arrays of Options with the query_as! macro // sqlx doesn't understand arrays of Options with the query_as! macro
&mut sqlx::query_as( &mut sqlx::query_as(
r#"INSERT INTO plugin_cells (plugin_id, cell_id, editor_id, created_at, updated_at) r#"INSERT INTO plugin_cells (plugin_id, cell_id, file_id, mod_id, editor_id, created_at, updated_at)
SELECT *, now(), now() FROM UNNEST($1::int[], $2::int[], $3::text[]) SELECT *, now(), now() FROM UNNEST($1::int[], $2::int[], $3::int[], $4::int[], $5::text[])
ON CONFLICT (plugin_id, cell_id) DO UPDATE ON CONFLICT (plugin_id, cell_id) DO UPDATE
SET (editor_id, updated_at) = (EXCLUDED.editor_id, now()) SET (file_id, mod_id, editor_id, updated_at) = (EXCLUDED.file_id, EXCLUDED.mod_id, EXCLUDED.editor_id, now())
RETURNING *"#, RETURNING *"#,
) )
.bind(&plugin_ids) .bind(&plugin_ids)
.bind(&cell_ids) .bind(&cell_ids)
.bind(&file_ids)
.bind(&mod_ids)
.bind(&editor_ids) .bind(&editor_ids)
.fetch_all(pool) .fetch_all(pool)
.await .await

View File

@ -9,8 +9,8 @@ use tracing::{info, warn};
use crate::models::file::File; use crate::models::file::File;
use crate::models::game_mod::Mod; use crate::models::game_mod::Mod;
use crate::models::{plugin, plugin::UnsavedPlugin};
use crate::models::{cell, cell::UnsavedCell}; use crate::models::{cell, cell::UnsavedCell};
use crate::models::{plugin, plugin::UnsavedPlugin};
use crate::models::{plugin_cell, plugin_cell::UnsavedPluginCell}; use crate::models::{plugin_cell, plugin_cell::UnsavedPluginCell};
use crate::models::{plugin_world, plugin_world::UnsavedPluginWorld}; use crate::models::{plugin_world, plugin_world::UnsavedPluginWorld};
use crate::models::{world, world::UnsavedWorld}; use crate::models::{world, world::UnsavedWorld};
@ -62,6 +62,7 @@ pub async fn process_plugin(
name: &db_file.name, name: &db_file.name,
hash: hash as i64, hash: hash as i64,
file_id: db_file.id, file_id: db_file.id,
mod_id: Some(db_mod.id),
version: plugin.header.version as f64, version: plugin.header.version as f64,
size: plugin_buf.len() as i64, size: plugin_buf.len() as i64,
author, author,
@ -133,6 +134,8 @@ pub async fn process_plugin(
.map(|(db_cell, plugin_cell)| UnsavedPluginCell { .map(|(db_cell, plugin_cell)| UnsavedPluginCell {
plugin_id: plugin_row.id, plugin_id: plugin_row.id,
cell_id: db_cell.id, cell_id: db_cell.id,
file_id: Some(db_file.id),
mod_id: Some(db_mod.id),
editor_id: plugin_cell.editor_id.as_ref().map(|id| id.as_ref()), editor_id: plugin_cell.editor_id.as_ref().map(|id| id.as_ref()),
}) })
.collect(); .collect();