Skip to content

Commit

Permalink
Use materialized view for summaries
Browse files Browse the repository at this point in the history
  • Loading branch information
WorkingRobot committed Dec 28, 2024
1 parent 7c6a9a9 commit 70f4a19
Show file tree
Hide file tree
Showing 13 changed files with 539 additions and 210 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions web/migrations/20241228203720_fix-indexes.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
DROP INDEX travel_states_time_idx;
ALTER TABLE travel_states DROP CONSTRAINT travel_states_pkey;
CREATE UNIQUE INDEX travel_states_pkey ON travel_states(world_id, time DESC);

DROP INDEX world_statuses_time_idx;
ALTER TABLE world_statuses DROP CONSTRAINT world_statuses_pkey;
CREATE UNIQUE INDEX world_statuses_pkey ON world_statuses(world_id, time DESC);
49 changes: 49 additions & 0 deletions web/migrations/20241228210331_summary-mview.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
CREATE MATERIALIZED VIEW IF NOT EXISTS world_summary AS
SELECT
w.world_id,
w.world_name,
w.datacenter_id,
w.datacenter_name,
w.region_id,
w.region_abbreviation,
w.region_name,
ws.status,
ws.category,
ws.can_create,
ts.prohibit,
qe.time,
qe.size,
qe.duration
FROM
worlds w
INNER JOIN (
SELECT
DISTINCT ON (world_id) world_id,
prohibit
FROM
travel_states
ORDER BY
world_id,
time DESC
) ts ON w.world_id = ts.world_id
INNER JOIN (
SELECT
DISTINCT ON (world_id) world_id,
status,
category,
can_create
FROM
world_statuses
ORDER BY
world_id,
time DESC
) ws ON w.world_id = ws.world_id
INNER JOIN (
SELECT
*
FROM
queue_estimates
) qe ON w.world_id = qe.world_id
WHERE
w.hidden = FALSE;

Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,25 @@ use crate::{await_cancellable, db};

use super::CronJob;

pub struct RefreshQueueEstimates {
pub struct RefreshMaterializedViews {
pool: PgPool,
}

impl RefreshQueueEstimates {
impl RefreshMaterializedViews {
pub fn new(pool: PgPool) -> Self {
Self { pool }
}
}

#[async_trait]
impl CronJob for RefreshQueueEstimates {
const NAME: &'static str = "refresh_queue_estimates";
impl CronJob for RefreshMaterializedViews {
const NAME: &'static str = "refresh_materialized_views";
const PERIOD: Duration = Duration::from_secs(60);

async fn run(&self, stop_signal: CancellationToken) -> anyhow::Result<()> {
let pool = &self.pool;
await_cancellable!(db::refresh_queue_estimates(pool), stop_signal);
await_cancellable!(db::refresh_world_summaries(pool), stop_signal);
Ok(())
}
}
10 changes: 1 addition & 9 deletions web/src/crons/refresh_travel_states.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,15 +129,7 @@ impl CronJob for RefreshTravelStates {
}
}

if let Some(t) = travel_time {
if t != data.average_elapsed_time {
log::error!("Travel time changed");
log::error!("Home {}: {}", data.home_world_id, data.average_elapsed_time);
log::error!("Old: {}", t);
}
} else {
travel_time = Some(data.average_elapsed_time);
}
travel_time = Some(data.average_elapsed_time);
}

if travel_map.is_empty() || travel_time.is_none() {
Expand Down
15 changes: 11 additions & 4 deletions web/src/db.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::{
db_wrappers::{DatabaseU16, DatabaseU64},
models::{
Connection, DbQueueEstimate, DbTravelState, DbWorldInfo, DbWorldStatus, QueueEstimate,
QueueSize, Recap,
Connection, DbQueueEstimate, DbTravelState, DbWorldStatus, DbWorldSummaryInfo,
QueueEstimate, QueueSize, Recap, WorldSummaryInfo,
},
};
use sqlx::{postgres::PgQueryResult, Error, PgPool, QueryBuilder};
Expand Down Expand Up @@ -483,8 +483,15 @@ pub async fn get_world_statuses_by_world_id(
.await
}

pub async fn get_world_info(pool: &PgPool) -> Result<Vec<DbWorldInfo>, Error> {
sqlx::query_as!(DbWorldInfo, r#"SELECT * FROM worlds WHERE hidden = FALSE"#)
pub async fn refresh_world_summaries(pool: &PgPool) -> Result<PgQueryResult, Error> {
sqlx::query!(r#"REFRESH MATERIALIZED VIEW CONCURRENTLY world_summary"#)
.execute(pool)
.await
}

pub async fn get_world_summaries(pool: &PgPool) -> Result<Vec<WorldSummaryInfo>, Error> {
sqlx::query_as!(DbWorldSummaryInfo, r#"SELECT * FROM world_summary"#)
.fetch_all(pool)
.await
.map(|summary| summary.into_iter().map(WorldSummaryInfo::from).collect())
}
2 changes: 1 addition & 1 deletion web/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ async fn main() -> Result<(), ServerError> {
.expect("Error creating reqwest client");

let refresh_queue_estimates_token =
crons::create_cron_job(crons::RefreshQueueEstimates::new(db_pool.clone()));
crons::create_cron_job(crons::RefreshMaterializedViews::new(db_pool.clone()));

let refresh_travel_states_token = crons::create_cron_job(
crons::RefreshTravelStates::new(config.stasis.clone(), db_pool.clone())
Expand Down
74 changes: 70 additions & 4 deletions web/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,15 +228,81 @@ pub struct DbWorldInfo {
pub hidden: bool,
}

#[derive(Serialize)]
#[derive(Debug, sqlx::FromRow)]
pub struct DbWorldSummaryInfo {
pub world_id: Option<i16>,
pub world_name: Option<String>,
pub datacenter_id: Option<i16>,
pub datacenter_name: Option<String>,
pub region_id: Option<i16>,
pub region_name: Option<String>,
pub region_abbreviation: Option<String>,

pub status: Option<i16>,
pub category: Option<i16>,
pub can_create: Option<bool>,

pub prohibit: Option<bool>,

pub time: Option<time::PrimitiveDateTime>,
pub size: Option<i32>,
pub duration: Option<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorldSummaryInfo {
pub world_id: u16,
pub world_name: String,
pub datacenter_id: u16,
pub datacenter_name: String,
pub region_id: u16,
pub region_name: String,
pub region_abbreviation: String,

pub status: i16,
pub category: i16,
pub can_create: bool,

pub travel_prohibit: bool,

pub queue_time: DatabaseDateTime,
pub queue_size: i32,
pub queue_duration: f64,
}

impl From<DbWorldSummaryInfo> for WorldSummaryInfo {
fn from(db: DbWorldSummaryInfo) -> Self {
Self {
world_id: db.world_id.unwrap_or_default() as u16,
world_name: db.world_name.unwrap_or_default(),
datacenter_id: db.datacenter_id.unwrap_or_default() as u16,
datacenter_name: db.datacenter_name.unwrap_or_default(),
region_id: db.region_id.unwrap_or_default() as u16,
region_name: db.region_name.unwrap_or_default(),
region_abbreviation: db.region_abbreviation.unwrap_or_default(),

status: db.status.unwrap_or_default(),
category: db.category.unwrap_or_default(),
can_create: db.can_create.unwrap_or_default(),

travel_prohibit: db.prohibit.unwrap_or_default(),

queue_time: DatabaseDateTime::from(db.time.unwrap_or(time::PrimitiveDateTime::MIN)),
queue_size: db.size.unwrap_or_default(),
queue_duration: db.duration.unwrap_or_default(),
}
}
}

#[derive(Serialize, Deserialize)]
pub struct Summary {
pub average_travel_time: i32,
pub worlds: Vec<WorldSummary>,
pub datacenters: Vec<DatacenterSummary>,
pub regions: Vec<RegionSummary>,
}

#[derive(Serialize)]
#[derive(Serialize, Deserialize)]
pub struct WorldSummary {
pub id: u16,
pub name: String,
Expand All @@ -252,7 +318,7 @@ pub struct WorldSummary {
pub queue_last_update: DatabaseDateTime,
}

#[derive(Serialize)]
#[derive(Serialize, Deserialize)]
pub struct DatacenterSummary {
pub id: u16,
pub name: String,
Expand All @@ -263,7 +329,7 @@ pub struct DatacenterSummary {
// pub open_ports: f32,
}

#[derive(Serialize)]
#[derive(Serialize, Deserialize)]
pub struct RegionSummary {
pub id: u16,
pub name: String,
Expand Down
Loading

0 comments on commit 70f4a19

Please sign in to comment.