Fast history no background (#5873)

* Finishing up post and comment actions

* Adding community_actions.

* instance and person actions

* Fixing person_actions.

* Fixing down migrations

* Adding person_content_combined.

* Search combined.

* Aggregates tables.

* Dont rename old tables

* Fixing some constraints.

* Convert bigints to ints

* Forgot a bigint.

* Rest of i64 -> i32

* Adding actions id columns.

* Fixing connection config.

* Formatting smoosh up.sql

* Use current_date instead of now

* Adding history tables for liked_combined

* Upping wal size

* Fix clippy

* Fixing clippy.

* Fixing i64

* Getting rid of let mut conn

* Adding the history status table.

* Adding published indexes to speed up history.

* Adding comment like history scanning.

* Fixing schema options setup.

* post_read history

* person_post_aggregates / read_comments history

* post_like history

* Fixing conflicts.

* Fixing clippy.

* Use constant batch_size.

* person content combined post and comment history.

* Fixing id scanning.

* post/comment_actions -> saved_combined history

* search history

* Post and comment aggregates -> post / comment history.

* Uncommenting full history building.

* Changing DB_BATCH_SIZE to i64

* Fixing clippy.

* Fix index names.

* Fixing diff check by removing indexes.

* Adding the uplete ignore actions::id columns.

* Fixing merge imports.

* Fixing submodule update

* Try trigger disabling.

* Fix clippy

* Remove history table, do faster bulk inserts. Smoosh first.

Comments about how this fast insert is done is within smoosh comments.

* Adding some timings.

* Fast person content combined history

* Adding search_combined

* Fix person saved combined unique names

* person_liked_combined

* Remove-aggregates

* Fixing up.sql issues

* Re-building schema.rs

* Fixing down migrations.

* Removing history updating.

* Format sql.

* Move postgres logging to customPostgresql.conf

* Try using postgres 16-alpine in CI

* Speeding up add_report_count.

* speed up inbox_combined

* Speeding up remove_post_sort_type_enums

* Fixing post_sort_type

* Speeding up person votes

* Fixing wrong conn.

* Fixing broken migrations

* Remove comment.

* Make sure to re-index table after re-enabling indexes.

* Removing id columns from actions tables.

* Fixing down migrations.

* Using create table as for smoosh migration

* create_table as for person_content_combined.

* Fixing person_content_combined uniques

* create table as for search_combined

* create table as for liked_combined

* create table as for inbox_combined.

* Fixing a few score types.

* Fixing id positions.
This commit is contained in:
Dessalines 2025-07-23 17:43:23 -04:00 committed by GitHub
parent b3e5e6c76a
commit a5b0475436
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
51 changed files with 1343 additions and 948 deletions

View file

@ -334,8 +334,7 @@ steps:
services:
database:
# 15-alpine image necessary because of diesel tests
image: pgautoupgrade/pgautoupgrade:15-alpine
image: pgautoupgrade/pgautoupgrade:16-alpine
environment:
POSTGRES_DB: lemmy
POSTGRES_USER: postgres

View file

@ -180,7 +180,7 @@ pub fn is_top_mod(
pub async fn update_read_comments(
person_id: PersonId,
post_id: PostId,
read_comments: i64,
read_comments: i32,
pool: &mut DbPool<'_>,
) -> LemmyResult<()> {
let person_post_agg_form = PostReadCommentsForm::new(post_id, person_id, read_comments);

View file

@ -7,6 +7,6 @@ use url::Url;
pub(crate) struct GroupFollowers {
pub(crate) id: Url,
pub(crate) r#type: CollectionType,
pub(crate) total_items: i64,
pub(crate) total_items: i32,
pub(crate) items: Vec<()>,
}

View file

@ -8,6 +8,6 @@ use url::Url;
pub struct GroupOutbox {
pub(crate) r#type: OrderedCollectionType,
pub(crate) id: Url,
pub(crate) total_items: i64,
pub(crate) total_items: i32,
pub(crate) ordered_items: Vec<AnnounceActivity>,
}

View file

@ -23,7 +23,7 @@ mod tests {
test_parse_lemmy_item::<GroupFollowers>("assets/lemmy/collections/group_followers.json")?;
let outbox =
test_parse_lemmy_item::<GroupOutbox>("assets/lemmy/collections/group_outbox.json")?;
assert_eq!(outbox.ordered_items.len() as i64, outbox.total_items);
assert_eq!(outbox.ordered_items.len(), outbox.total_items as usize);
test_parse_lemmy_item::<GroupFeatured>("assets/lemmy/collections/group_featured_posts.json")?;
test_parse_lemmy_item::<GroupModerators>("assets/lemmy/collections/group_moderators.json")?;
test_parse_lemmy_item::<EmptyOutbox>("assets/lemmy/collections/person_outbox.json")?;

View file

@ -284,7 +284,7 @@ impl Community {
pub async fn update_federated_followers(
pool: &mut DbPool<'_>,
for_community_id: CommunityId,
new_subscribers: i64,
new_subscribers: i32,
) -> LemmyResult<Self> {
let conn = &mut get_conn(pool).await?;
diesel::update(community::table.find(for_community_id))

View file

@ -85,10 +85,10 @@ impl Instance {
instance_id: InstanceId,
form: InstanceForm,
) -> LemmyResult<usize> {
let mut conn = get_conn(pool).await?;
let conn = &mut get_conn(pool).await?;
diesel::update(instance::table.find(instance_id))
.set(form)
.execute(&mut conn)
.execute(conn)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateSite)
}

View file

@ -308,7 +308,7 @@ impl Post {
.select(community::interactions_month)
.inner_join(post::table.on(community::id.eq(post::community_id)))
.filter(post::id.eq(post_id))
.first::<i64>(conn)
.first::<i32>(conn)
.await?;
diesel::update(post::table.find(post_id))
@ -520,13 +520,6 @@ impl PostActions {
}
impl PostActions {
pub fn build_many_read_forms(post_ids: &[PostId], person_id: PersonId) -> Vec<PostReadForm> {
post_ids
.iter()
.map(|post_id| (PostReadForm::new(*post_id, person_id)))
.collect::<Vec<_>>()
}
pub async fn read(
pool: &mut DbPool<'_>,
post_id: PostId,
@ -553,6 +546,13 @@ impl PostActions {
Self::read(pool, PostId(*post_id), PersonId(*person_id)).await
}
pub fn build_many_read_forms(post_ids: &[PostId], person_id: PersonId) -> Vec<PostReadForm> {
post_ids
.iter()
.map(|post_id| (PostReadForm::new(*post_id, person_id)))
.collect::<Vec<_>>()
}
pub async fn update_notification_state(
post_id: PostId,
person_id: PersonId,

View file

@ -18,8 +18,22 @@ use serde_with::skip_serializing_none;
#[cfg_attr(feature = "full", cursor_keys_module(name = person_content_combined_keys))]
/// A combined table for a persons contents (posts and comments)
pub struct PersonContentCombined {
pub id: PersonContentCombinedId,
pub published_at: DateTime<Utc>,
pub post_id: Option<PostId>,
pub comment_id: Option<CommentId>,
pub id: PersonContentCombinedId,
}
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = person_content_combined))]
pub struct PersonContentCombinedPostInsertForm {
pub post_id: PostId,
pub published_at: DateTime<Utc>,
}
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = person_content_combined))]
pub struct PersonContentCombinedCommentInsertForm {
pub comment_id: CommentId,
pub published_at: DateTime<Utc>,
}

View file

@ -18,10 +18,10 @@ use serde_with::skip_serializing_none;
#[cfg_attr(feature = "full", cursor_keys_module(name = person_liked_combined_keys))]
/// A combined person_liked table.
pub struct PersonLikedCombined {
pub id: PersonLikedCombinedId,
pub liked_at: DateTime<Utc>,
pub like_score: i16,
pub person_id: PersonId,
pub post_id: Option<PostId>,
pub comment_id: Option<CommentId>,
pub id: PersonLikedCombinedId,
}

View file

@ -18,9 +18,25 @@ use serde_with::skip_serializing_none;
#[cfg_attr(feature = "full", cursor_keys_module(name = person_saved_combined_keys))]
/// A combined person_saved table.
pub struct PersonSavedCombined {
pub id: PersonSavedCombinedId,
pub saved_at: DateTime<Utc>,
pub person_id: PersonId,
pub post_id: Option<PostId>,
pub comment_id: Option<CommentId>,
pub id: PersonSavedCombinedId,
}
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = person_saved_combined))]
pub struct PersonSavedCombinedPostInsertForm {
pub saved_at: DateTime<Utc>,
pub person_id: PersonId,
pub post_id: PostId,
}
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = person_saved_combined))]
pub struct PersonSavedCombinedCommentInsertForm {
pub saved_at: DateTime<Utc>,
pub person_id: PersonId,
pub comment_id: CommentId,
}

View file

@ -25,12 +25,44 @@ use serde_with::skip_serializing_none;
#[cfg_attr(feature = "full", cursor_keys_module(name = search_combined_keys))]
/// A combined table for a search (posts, comments, communities, persons)
pub struct SearchCombined {
pub id: SearchCombinedId,
pub published_at: DateTime<Utc>,
pub score: i64,
pub score: i32,
pub post_id: Option<PostId>,
pub comment_id: Option<CommentId>,
pub community_id: Option<CommunityId>,
pub person_id: Option<PersonId>,
pub id: SearchCombinedId,
pub multi_community_id: Option<MultiCommunityId>,
}
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = search_combined))]
pub struct SearchCombinedPostInsertForm {
pub published_at: DateTime<Utc>,
pub score: i32,
pub post_id: PostId,
}
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = search_combined))]
pub struct SearchCombinedCommentInsertForm {
pub published_at: DateTime<Utc>,
pub score: i32,
pub comment_id: CommentId,
}
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = search_combined))]
pub struct SearchCombinedCommunityInsertForm {
pub published_at: DateTime<Utc>,
pub score: i32,
pub community_id: CommunityId,
}
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = search_combined))]
pub struct SearchCombinedPersonInsertForm {
pub published_at: DateTime<Utc>,
pub score: i32,
pub person_id: PersonId,
}

View file

@ -49,9 +49,9 @@ pub struct Comment {
/// Whether the comment has been distinguished(speaking officially) by a mod.
pub distinguished: bool,
pub language_id: LanguageId,
pub score: i64,
pub upvotes: i64,
pub downvotes: i64,
pub score: i32,
pub upvotes: i32,
pub downvotes: i32,
/// The total number of children in this comment branch.
pub child_count: i32,
#[serde(skip)]

View file

@ -78,25 +78,25 @@ pub struct Community {
pub description: Option<String>,
#[serde(skip)]
pub random_number: i16,
pub subscribers: i64,
pub posts: i64,
pub comments: i64,
pub subscribers: i32,
pub posts: i32,
pub comments: i32,
/// The number of users with any activity in the last day.
pub users_active_day: i64,
pub users_active_day: i32,
/// The number of users with any activity in the last week.
pub users_active_week: i64,
pub users_active_week: i32,
/// The number of users with any activity in the last month.
pub users_active_month: i64,
pub users_active_month: i32,
/// The number of users with any activity in the last year.
pub users_active_half_year: i64,
pub users_active_half_year: i32,
#[serde(skip)]
pub hot_rank: f64,
pub subscribers_local: i64,
pub subscribers_local: i32,
pub report_count: i16,
pub unresolved_report_count: i16,
/// Number of any interactions over the last month.
#[serde(skip)]
pub interactions_month: i64,
pub interactions_month: i32,
pub local_removed: bool,
}
@ -195,10 +195,10 @@ pub struct CommunityUpdateForm {
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[cfg_attr(feature = "ts-rs", ts(optional_fields, export))]
pub struct CommunityActions {
#[serde(skip)]
pub community_id: CommunityId,
#[serde(skip)]
pub person_id: PersonId,
#[serde(skip)]
pub community_id: CommunityId,
/// When the community was followed.
pub followed_at: Option<DateTime<Utc>>,
/// The state of the community follow.

View file

@ -80,18 +80,18 @@ pub struct LocalSite {
pub default_post_time_range_seconds: Option<i32>,
/// Block NSFW content being created
pub disallow_nsfw_content: bool,
pub users: i64,
pub posts: i64,
pub comments: i64,
pub communities: i64,
pub users: i32,
pub posts: i32,
pub comments: i32,
pub communities: i32,
/// The number of users with any activity in the last day.
pub users_active_day: i64,
pub users_active_day: i32,
/// The number of users with any activity in the last week.
pub users_active_week: i64,
pub users_active_week: i32,
/// The number of users with any activity in the last month.
pub users_active_month: i64,
pub users_active_month: i32,
/// The number of users with any activity in the last half year.
pub users_active_half_year: i64,
pub users_active_half_year: i32,
/// Dont send email notifications to users for new replies, mentions etc
pub disable_email_notifications: bool,
pub suggested_communities: Option<MultiCommunityId>,

View file

@ -56,12 +56,12 @@ pub struct Person {
/// Whether the person is a bot account.
pub bot_account: bool,
pub instance_id: InstanceId,
pub post_count: i64,
pub post_count: i32,
#[serde(skip)]
pub post_score: i64,
pub comment_count: i64,
pub post_score: i32,
pub comment_count: i32,
#[serde(skip)]
pub comment_score: i64,
pub comment_score: i32,
}
#[derive(Clone, derive_new::new)]
@ -134,11 +134,11 @@ pub struct PersonUpdateForm {
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[cfg_attr(feature = "ts-rs", ts(optional_fields, export))]
pub struct PersonActions {
#[serde(skip)]
pub target_id: PersonId,
#[serde(skip)]
pub person_id: PersonId,
#[serde(skip)]
pub target_id: PersonId,
#[serde(skip)]
pub followed_at: Option<DateTime<Utc>>,
#[serde(skip)]
pub follow_pending: Option<bool>,

View file

@ -62,10 +62,10 @@ pub struct Post {
pub alt_text: Option<String>,
/// Time at which the post will be published. None means publish immediately.
pub scheduled_publish_time_at: Option<DateTime<Utc>>,
pub comments: i64,
pub score: i64,
pub upvotes: i64,
pub downvotes: i64,
pub comments: i32,
pub score: i32,
pub upvotes: i32,
pub downvotes: i32,
#[serde(skip)]
/// A newest comment time, limited to 2 days, to prevent necrobumping
pub newest_comment_time_necro_at: DateTime<Utc>,
@ -183,17 +183,17 @@ pub struct PostUpdateForm {
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[cfg_attr(feature = "ts-rs", ts(optional_fields, export))]
pub struct PostActions {
#[serde(skip)]
pub post_id: PostId,
#[serde(skip)]
pub person_id: PersonId,
#[serde(skip)]
pub post_id: PostId,
/// When the post was read.
pub read_at: Option<DateTime<Utc>>,
/// When was the last time you read the comments.
pub read_comments_at: Option<DateTime<Utc>>,
/// The number of comments you read last. Subtract this from total comments to get an unread
/// count.
pub read_comments_amount: Option<i64>,
pub read_comments_amount: Option<i32>,
/// When the post was saved.
pub saved_at: Option<DateTime<Utc>>,
/// When the post was liked.
@ -245,7 +245,7 @@ pub struct PostReadForm {
pub struct PostReadCommentsForm {
pub post_id: PostId,
pub person_id: PersonId,
pub read_comments_amount: i64,
pub read_comments_amount: i32,
#[new(value = "Utc::now()")]
pub read_comments_at: DateTime<Utc>,
}

View file

@ -50,7 +50,7 @@ use rustls::{
};
use std::{
ops::{Deref, DerefMut},
sync::{Arc, OnceLock},
sync::Arc,
time::Duration,
};
use tracing::error;
@ -62,8 +62,6 @@ pub const SITEMAP_LIMIT: i64 = 50000;
pub const SITEMAP_DAYS: TimeDelta = TimeDelta::days(31);
pub const RANK_DEFAULT: f64 = 0.0001;
/// Some connection options to speed up queries
const CONNECTION_OPTIONS: [&str; 1] = ["geqo_threshold=12"];
pub type ActualDbPool = Pool<AsyncPgConnection>;
/// References a pool or connection. Functions must take `&mut DbPool<'_>` to allow implicit
@ -370,38 +368,8 @@ pub fn diesel_url_create(opt: Option<&str>) -> LemmyResult<Option<DbUrl>> {
}
}
/// Sets a few additional config options necessary for starting lemmy
fn build_config_options_uri_segment(config: &str) -> LemmyResult<String> {
let mut url = Url::parse(config)?;
// Set `lemmy.protocol_and_hostname` so triggers can use it
let lemmy_protocol_and_hostname_option =
"lemmy.protocol_and_hostname=".to_owned() + &SETTINGS.get_protocol_and_hostname();
let mut options = CONNECTION_OPTIONS.to_vec();
options.push(&lemmy_protocol_and_hostname_option);
// Create the connection uri portion
let options_segments = options
.iter()
.map(|o| "-c ".to_owned() + o)
.collect::<Vec<String>>()
.join(" ");
url.set_query(Some(&format!("options={options_segments}")));
Ok(url.into())
}
fn establish_connection(config: &str) -> BoxFuture<'_, ConnectionResult<AsyncPgConnection>> {
let fut = async {
/// Use a once_lock to create the postgres connection config, since this config never changes
static POSTGRES_CONFIG_WITH_OPTIONS: OnceLock<String> = OnceLock::new();
let config = POSTGRES_CONFIG_WITH_OPTIONS.get_or_init(|| {
build_config_options_uri_segment(config)
.inspect_err(|e| error!("Couldn't parse postgres connection URI: {e}"))
.unwrap_or_default()
});
// We only support TLS with sslmode=require currently
let conn = if config.contains("sslmode=require") {
let rustls_config = DangerousClientConfigBuilder {
@ -481,7 +449,7 @@ impl ServerCertVerifier for NoCertVerifier {
}
pub fn build_db_pool() -> LemmyResult<ActualDbPool> {
let db_url = SETTINGS.get_database_url();
let db_url = SETTINGS.get_database_url_with_options()?;
// diesel-async does not support any TLS connections out of the box, so we need to manually
// provide a setup function which handles creating the connection
let mut config = ManagerConfig::default();
@ -515,16 +483,16 @@ pub fn build_db_pool_for_tests() -> ActualDbPool {
}
pub mod functions {
use diesel::sql_types::{BigInt, Text, Timestamptz};
use diesel::sql_types::{Int4, Text, Timestamptz};
define_sql_function! {
#[sql_name = "r.hot_rank"]
fn hot_rank(score: BigInt, time: Timestamptz) -> Double;
fn hot_rank(score: Int4, time: Timestamptz) -> Double;
}
define_sql_function! {
#[sql_name = "r.scaled_rank"]
fn scaled_rank(score: BigInt, time: Timestamptz, interactions_month: BigInt) -> Double;
fn scaled_rank(score: Int4, time: Timestamptz, interactions_month: Int4) -> Double;
}
define_sql_function!(fn lower(x: Text) -> Text);

View file

@ -146,9 +146,9 @@ diesel::table! {
path -> Ltree,
distinguished -> Bool,
language_id -> Int4,
score -> Int8,
upvotes -> Int8,
downvotes -> Int8,
score -> Int4,
upvotes -> Int4,
downvotes -> Int4,
child_count -> Int4,
hot_rank -> Float8,
controversy_rank -> Float8,
@ -221,18 +221,18 @@ diesel::table! {
#[max_length = 150]
description -> Nullable<Varchar>,
random_number -> Int2,
subscribers -> Int8,
posts -> Int8,
comments -> Int8,
users_active_day -> Int8,
users_active_week -> Int8,
users_active_month -> Int8,
users_active_half_year -> Int8,
subscribers -> Int4,
posts -> Int4,
comments -> Int4,
users_active_day -> Int4,
users_active_week -> Int4,
users_active_month -> Int4,
users_active_half_year -> Int4,
hot_rank -> Float8,
subscribers_local -> Int8,
subscribers_local -> Int4,
report_count -> Int2,
unresolved_report_count -> Int2,
interactions_month -> Int8,
interactions_month -> Int4,
local_removed -> Bool,
}
}
@ -243,8 +243,8 @@ diesel::table! {
use super::sql_types::CommunityNotificationsModeEnum;
community_actions (person_id, community_id) {
community_id -> Int4,
person_id -> Int4,
community_id -> Int4,
followed_at -> Nullable<Timestamptz>,
follow_state -> Nullable<CommunityFollowerState>,
follow_approver_id -> Nullable<Int4>,
@ -257,9 +257,10 @@ diesel::table! {
}
diesel::table! {
community_community_follow (target_id, community_id) {
community_community_follow (community_id, target_id) {
target_id -> Int4,
community_id -> Int4,
published_at -> Timestamptz,
}
}
@ -442,14 +443,14 @@ diesel::table! {
comment_downvotes -> FederationModeEnum,
default_post_time_range_seconds -> Nullable<Int4>,
disallow_nsfw_content -> Bool,
users -> Int8,
posts -> Int8,
comments -> Int8,
communities -> Int8,
users_active_day -> Int8,
users_active_week -> Int8,
users_active_month -> Int8,
users_active_half_year -> Int8,
users -> Int4,
posts -> Int4,
comments -> Int4,
communities -> Int4,
users_active_day -> Int4,
users_active_week -> Int4,
users_active_month -> Int4,
users_active_half_year -> Int4,
disable_email_notifications -> Bool,
suggested_communities -> Nullable<Int4>,
multi_comm_follower -> Int4,
@ -832,17 +833,17 @@ diesel::table! {
matrix_user_id -> Nullable<Text>,
bot_account -> Bool,
instance_id -> Int4,
post_count -> Int8,
post_score -> Int8,
comment_count -> Int8,
comment_score -> Int8,
post_count -> Int4,
post_score -> Int4,
comment_count -> Int4,
comment_score -> Int4,
}
}
diesel::table! {
person_actions (person_id, target_id) {
target_id -> Int4,
person_id -> Int4,
target_id -> Int4,
followed_at -> Nullable<Timestamptz>,
follow_pending -> Nullable<Bool>,
blocked_at -> Nullable<Timestamptz>,
@ -856,31 +857,31 @@ diesel::table! {
diesel::table! {
person_content_combined (id) {
id -> Int4,
published_at -> Timestamptz,
post_id -> Nullable<Int4>,
comment_id -> Nullable<Int4>,
id -> Int4,
}
}
diesel::table! {
person_liked_combined (id) {
id -> Int4,
liked_at -> Timestamptz,
like_score -> Int2,
person_id -> Int4,
post_id -> Nullable<Int4>,
comment_id -> Nullable<Int4>,
id -> Int4,
}
}
diesel::table! {
person_saved_combined (id) {
id -> Int4,
saved_at -> Timestamptz,
person_id -> Int4,
post_id -> Nullable<Int4>,
comment_id -> Nullable<Int4>,
id -> Int4,
}
}
@ -913,10 +914,10 @@ diesel::table! {
url_content_type -> Nullable<Text>,
alt_text -> Nullable<Text>,
scheduled_publish_time_at -> Nullable<Timestamptz>,
comments -> Int8,
score -> Int8,
upvotes -> Int8,
downvotes -> Int8,
comments -> Int4,
score -> Int4,
upvotes -> Int4,
downvotes -> Int4,
newest_comment_time_necro_at -> Timestamptz,
newest_comment_time_at -> Timestamptz,
hot_rank -> Float8,
@ -934,11 +935,11 @@ diesel::table! {
use super::sql_types::PostNotificationsModeEnum;
post_actions (person_id, post_id) {
post_id -> Int4,
person_id -> Int4,
post_id -> Int4,
read_at -> Nullable<Timestamptz>,
read_comments_at -> Nullable<Timestamptz>,
read_comments_amount -> Nullable<Int8>,
read_comments_amount -> Nullable<Int4>,
saved_at -> Nullable<Timestamptz>,
liked_at -> Nullable<Timestamptz>,
like_score -> Nullable<Int2>,
@ -1041,13 +1042,13 @@ diesel::table! {
diesel::table! {
search_combined (id) {
id -> Int4,
published_at -> Timestamptz,
score -> Int8,
score -> Int4,
post_id -> Nullable<Int4>,
comment_id -> Nullable<Int4>,
community_id -> Nullable<Int4>,
person_id -> Nullable<Int4>,
id -> Int4,
multi_community_id -> Nullable<Int4>,
}
}

View file

@ -184,7 +184,7 @@ pub enum Branch {
pub fn run(options: Options, db_url: &str) -> anyhow::Result<Branch> {
// Migrations don't support async connection, and this function doesn't need to be async
let mut conn = PgConnection::establish(db_url)?;
let conn = &mut PgConnection::establish(db_url)?;
// If possible, skip getting a lock and recreating the "r" schema, so
// lemmy_server processes in a horizontally scaled setup can start without causing locks
@ -203,7 +203,7 @@ pub fn run(options: Options, db_url: &str) -> anyhow::Result<Branch> {
let schema_exists = exists(pg_namespace::table.find("r"));
if select(sql_unchanged.and(schema_exists)).get_result(&mut conn)? {
if select(sql_unchanged.and(schema_exists)).get_result(conn)? {
return Ok(Branch::EarlyReturn);
}
}
@ -216,9 +216,9 @@ pub fn run(options: Options, db_url: &str) -> anyhow::Result<Branch> {
// Drop `r` schema, so migrations don't need to be made to work both with and without things in
// it existing
revert_replaceable_schema(&mut conn)?;
revert_replaceable_schema(conn)?;
run_selected_migrations(&mut conn, &options).map_err(convert_err)?;
run_selected_migrations(conn, &options).map_err(convert_err)?;
// Only run replaceable_schema if newest migration was applied
let output = if (options.run && options.limit.is_none())
@ -230,8 +230,8 @@ pub fn run(options: Options, db_url: &str) -> anyhow::Result<Branch> {
if options.enable_diff_check {
let before = diff_check::get_dump();
run_replaceable_schema(&mut conn)?;
revert_replaceable_schema(&mut conn)?;
run_replaceable_schema(conn)?;
revert_replaceable_schema(conn)?;
let after = diff_check::get_dump();
@ -240,7 +240,7 @@ pub fn run(options: Options, db_url: &str) -> anyhow::Result<Branch> {
diff_check::deferr_constraint_check(&after);
}
run_replaceable_schema(&mut conn)?;
run_replaceable_schema(conn)?;
Branch::ReplaceableSchemaRebuilt
} else {
@ -383,7 +383,7 @@ mod tests {
fn test_schema_setup() -> LemmyResult<()> {
let o = Options::default();
let db_url = SETTINGS.get_database_url();
let mut conn = PgConnection::establish(&db_url)?;
let conn = &mut PgConnection::establish(&db_url)?;
// Start with consistent state by dropping everything
conn.batch_execute("DROP OWNED BY CURRENT_USER;")?;
@ -395,7 +395,7 @@ mod tests {
);
// Insert the test data
insert_test_data(&mut conn)?;
insert_test_data(conn)?;
// Run all migrations, and make sure that changes can be correctly reverted
assert_eq!(
@ -404,11 +404,11 @@ mod tests {
);
// Check the test data we inserted before after running migrations
check_test_data(&mut conn)?;
check_test_data(conn)?;
// Check the current schema
assert_eq!(
get_foreign_keys_with_missing_indexes(&mut conn)?,
get_foreign_keys_with_missing_indexes(conn)?,
Vec::<String>::new(),
"each foreign key needs an index so that deleting the referenced row does not scan the whole referencing table"
);
@ -595,7 +595,7 @@ mod tests {
assert_eq!(posts[0].4, TEST_COMMUNITY_ID_1);
assert_eq!(posts[0].5, TEST_USER_ID_1);
let comments: Vec<(i32, String, String, i32, i32, Ltree, i64)> = comment::table
let comments: Vec<(i32, String, String, i32, i32, Ltree, i32)> = comment::table
.select((
comment::id,
comment::content,

View file

@ -66,13 +66,14 @@ impl CommunityFollowerView {
pub async fn count_community_followers(
pool: &mut DbPool<'_>,
community_id: CommunityId,
) -> LemmyResult<i64> {
) -> LemmyResult<i32> {
let conn = &mut get_conn(pool).await?;
Self::joins()
.filter(community_actions::community_id.eq(community_id))
.select(count_star())
.first::<i64>(conn)
.await
.map(i32::try_from)?
.with_lemmy_type(LemmyErrorType::NotFound)
}

@ -1 +1 @@
Subproject commit 72c9cc342b339779cd6d61a8e3349aeff5cad2ff
Subproject commit 29ffa99df0f487fda2037f49e8683129cedae066

View file

@ -115,16 +115,16 @@ pub struct NodeInfoSoftware {
#[serde(rename_all = "camelCase", default)]
pub struct NodeInfoUsage {
pub users: Option<NodeInfoUsers>,
pub local_posts: Option<i64>,
pub local_comments: Option<i64>,
pub local_posts: Option<i32>,
pub local_comments: Option<i32>,
}
#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct NodeInfoUsers {
pub total: Option<i64>,
pub active_halfyear: Option<i64>,
pub active_month: Option<i64>,
pub total: Option<i32>,
pub active_halfyear: Option<i32>,
pub active_month: Option<i32>,
}
#[derive(Serialize, Deserialize, Debug, Default)]

View file

@ -6,7 +6,7 @@ use diesel::{
dsl::{count, exists, not, update, IntervalDsl},
query_builder::AsQuery,
sql_query,
sql_types::{Integer, Timestamptz},
sql_types::{BigInt, Timestamptz},
BoolExpressionMethods,
ExpressionMethods,
NullableExpressionMethods,
@ -47,7 +47,10 @@ use lemmy_db_schema_file::schema::{
site,
};
use lemmy_db_views_site::SiteView;
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
use lemmy_utils::{
error::{LemmyErrorType, LemmyResult},
DB_BATCH_SIZE,
};
use reqwest_middleware::ClientWithMiddleware;
use std::time::Duration;
use tracing::{info, warn};
@ -140,12 +143,12 @@ pub async fn setup(context: Data<LemmyContext>) -> LemmyResult<()> {
async fn update_hot_ranks(pool: &mut DbPool<'_>) -> LemmyResult<()> {
info!("Updating hot ranks for all history...");
let mut conn = get_conn(pool).await?;
let conn = &mut get_conn(pool).await?;
process_post_aggregates_ranks_in_batches(&mut conn).await?;
process_post_aggregates_ranks_in_batches(conn).await?;
process_ranks_in_batches(
&mut conn,
conn,
"comment",
"a.hot_rank != 0",
"SET hot_rank = r.hot_rank(a.score, a.published_at)",
@ -153,7 +156,7 @@ async fn update_hot_ranks(pool: &mut DbPool<'_>) -> LemmyResult<()> {
.await?;
process_ranks_in_batches(
&mut conn,
conn,
"community",
"a.hot_rank != 0",
"SET hot_rank = r.hot_rank(a.subscribers, a.published_at)",
@ -182,7 +185,6 @@ async fn process_ranks_in_batches(
) -> LemmyResult<()> {
let process_start_time: DateTime<Utc> = Utc.timestamp_opt(0, 0).single().unwrap_or_default();
let update_batch_size = 1000; // Bigger batches than this tend to cause seq scans
let mut processed_rows_count = 0;
let mut previous_batch_result = Some(process_start_time);
while let Some(previous_batch_last_published) = previous_batch_result {
@ -200,7 +202,7 @@ async fn process_ranks_in_batches(
"#,
))
.bind::<Timestamptz, _>(previous_batch_last_published)
.bind::<Integer, _>(update_batch_size)
.bind::<BigInt, _>(DB_BATCH_SIZE)
.get_results::<HotRanksUpdateResult>(conn)
.await
.map_err(|e| {
@ -222,7 +224,6 @@ async fn process_ranks_in_batches(
async fn process_post_aggregates_ranks_in_batches(conn: &mut AsyncPgConnection) -> LemmyResult<()> {
let process_start_time: DateTime<Utc> = Utc.timestamp_opt(0, 0).single().unwrap_or_default();
let update_batch_size = 1000; // Bigger batches than this tend to cause seq scans
let mut processed_rows_count = 0;
let mut previous_batch_result = Some(process_start_time);
while let Some(previous_batch_last_published) = previous_batch_result {
@ -245,7 +246,7 @@ async fn process_post_aggregates_ranks_in_batches(conn: &mut AsyncPgConnection)
"#,
)
.bind::<Timestamptz, _>(previous_batch_last_published)
.bind::<Integer, _>(update_batch_size)
.bind::<BigInt, _>(DB_BATCH_SIZE)
.get_results::<HotRanksUpdateResult>(conn)
.await
.map_err(|e| {
@ -263,12 +264,12 @@ async fn process_post_aggregates_ranks_in_batches(conn: &mut AsyncPgConnection)
}
async fn delete_expired_captcha_answers(pool: &mut DbPool<'_>) -> LemmyResult<()> {
let mut conn = get_conn(pool).await?;
let conn = &mut get_conn(pool).await?;
diesel::delete(
captcha_answer::table.filter(captcha_answer::published_at.lt(now() - IntervalDsl::minutes(10))),
)
.execute(&mut conn)
.execute(conn)
.await?;
info!("Done.");
@ -278,19 +279,19 @@ async fn delete_expired_captcha_answers(pool: &mut DbPool<'_>) -> LemmyResult<()
/// Clear old activities (this table gets very large)
async fn clear_old_activities(pool: &mut DbPool<'_>) -> LemmyResult<()> {
info!("Clearing old activities...");
let mut conn = get_conn(pool).await?;
let conn = &mut get_conn(pool).await?;
diesel::delete(
sent_activity::table.filter(sent_activity::published_at.lt(now() - IntervalDsl::days(7))),
)
.execute(&mut conn)
.execute(conn)
.await?;
diesel::delete(
received_activity::table
.filter(received_activity::published_at.lt(now() - IntervalDsl::days(7))),
)
.execute(&mut conn)
.execute(conn)
.await?;
info!("Done.");
Ok(())
@ -305,7 +306,7 @@ async fn delete_old_denied_users(pool: &mut DbPool<'_>) -> LemmyResult<()> {
/// overwrite posts and comments 30d after deletion
async fn overwrite_deleted_posts_and_comments(pool: &mut DbPool<'_>) -> LemmyResult<()> {
info!("Overwriting deleted posts...");
let mut conn = get_conn(pool).await?;
let conn = &mut get_conn(pool).await?;
diesel::update(
post::table
@ -317,7 +318,7 @@ async fn overwrite_deleted_posts_and_comments(pool: &mut DbPool<'_>) -> LemmyRes
post::body.eq(DELETED_REPLACEMENT_TEXT),
post::name.eq(DELETED_REPLACEMENT_TEXT),
))
.execute(&mut conn)
.execute(conn)
.await?;
info!("Overwriting deleted comments...");
@ -328,7 +329,7 @@ async fn overwrite_deleted_posts_and_comments(pool: &mut DbPool<'_>) -> LemmyRes
.filter(comment::content.ne(DELETED_REPLACEMENT_TEXT)),
)
.set(comment::content.eq(DELETED_REPLACEMENT_TEXT))
.execute(&mut conn)
.execute(conn)
.await?;
info!("Done.");
Ok(())
@ -338,7 +339,7 @@ async fn overwrite_deleted_posts_and_comments(pool: &mut DbPool<'_>) -> LemmyRes
async fn active_counts(pool: &mut DbPool<'_>) -> LemmyResult<()> {
info!("Updating active site and community aggregates ...");
let mut conn = get_conn(pool).await?;
let conn = &mut get_conn(pool).await?;
let intervals = vec![
("1 day", "day"),
@ -352,20 +353,16 @@ async fn active_counts(pool: &mut DbPool<'_>) -> LemmyResult<()> {
"update local_site set users_active_{} = (select r.site_aggregates_activity('{}')) where site_id = 1",
abbr, full_form
);
sql_query(update_site_stmt).execute(&mut conn).await?;
sql_query(update_site_stmt).execute(conn).await?;
let update_community_stmt = format!("update community ca set users_active_{} = mv.count_ from r.community_aggregates_activity('{}') mv where ca.id = mv.community_id_", abbr, full_form);
sql_query(update_community_stmt).execute(&mut conn).await?;
sql_query(update_community_stmt).execute(conn).await?;
}
let update_interactions_stmt = "update community ca set interactions_month = mv.count_ from r.community_aggregates_interactions('1 month') mv where ca.id = mv.community_id_";
sql_query(update_interactions_stmt)
.execute(&mut conn)
.await?;
sql_query(update_interactions_stmt).execute(conn).await?;
let mut conn = get_conn(pool).await?;
let user_count: i64 = local_user::table
let user_count = local_user::table
.inner_join(
person::table.left_join(
instance_actions::table
@ -378,12 +375,13 @@ async fn active_counts(pool: &mut DbPool<'_>) -> LemmyResult<()> {
.filter(instance_actions::received_ban_at.is_null())
.filter(not(person::deleted))
.select(count(local_user::id))
.get_result(&mut conn)
.await?;
.first::<i64>(conn)
.await
.map(i32::try_from)??;
update(local_site::table)
.set((local_site::users.eq(user_count),))
.execute(&mut conn)
.set(local_site::users.eq(user_count))
.execute(conn)
.await?;
info!("Done.");
@ -393,20 +391,20 @@ async fn active_counts(pool: &mut DbPool<'_>) -> LemmyResult<()> {
/// Set banned to false after ban expires
async fn update_banned_when_expired(pool: &mut DbPool<'_>) -> LemmyResult<()> {
info!("Updating banned column if it expires ...");
let mut conn = get_conn(pool).await?;
let conn = &mut get_conn(pool).await?;
uplete(community_actions::table.filter(community_actions::ban_expires_at.lt(now().nullable())))
.set_null(community_actions::received_ban_at)
.set_null(community_actions::ban_expires_at)
.as_query()
.execute(&mut conn)
.execute(conn)
.await?;
uplete(instance_actions::table.filter(instance_actions::ban_expires_at.lt(now().nullable())))
.set_null(instance_actions::received_ban_at)
.set_null(instance_actions::ban_expires_at)
.as_query()
.execute(&mut conn)
.execute(conn)
.await?;
Ok(())
}
@ -414,12 +412,12 @@ async fn update_banned_when_expired(pool: &mut DbPool<'_>) -> LemmyResult<()> {
/// Set banned to false after ban expires
async fn delete_instance_block_when_expired(pool: &mut DbPool<'_>) -> LemmyResult<()> {
info!("Delete instance blocks when expired ...");
let mut conn = get_conn(pool).await?;
let conn = &mut get_conn(pool).await?;
diesel::delete(
federation_blocklist::table.filter(federation_blocklist::expires_at.lt(now().nullable())),
)
.execute(&mut conn)
.execute(conn)
.await?;
Ok(())
}
@ -428,7 +426,7 @@ async fn delete_instance_block_when_expired(pool: &mut DbPool<'_>) -> LemmyResul
async fn publish_scheduled_posts(context: &Data<LemmyContext>) -> LemmyResult<()> {
let pool = &mut context.pool();
let local_instance_id = SiteView::read_local(pool).await?.instance.id;
let mut conn = get_conn(pool).await?;
let conn = &mut get_conn(pool).await?;
let not_community_banned_action = community_actions::table
.find((person::id, community::id))
@ -453,7 +451,7 @@ async fn publish_scheduled_posts(context: &Data<LemmyContext>) -> LemmyResult<()
// ensure that user isnt banned from local
.filter(not(exists(not_local_banned_action)))
.select((post::all_columns, community::all_columns))
.get_results::<(Post, Community)>(&mut conn)
.get_results::<(Post, Community)>(conn)
.await?;
for (post, community) in scheduled_posts {
@ -483,9 +481,9 @@ async fn update_instance_software(
client: &ClientWithMiddleware,
) -> LemmyResult<()> {
info!("Updating instances software and versions...");
let mut conn = get_conn(pool).await?;
let conn = &mut get_conn(pool).await?;
let instances = instance::table.get_results::<Instance>(&mut conn).await?;
let instances = instance::table.get_results::<Instance>(conn).await?;
for instance in instances {
if let Some(form) = build_update_instance_form(&instance.domain, client).await {

View file

@ -49,6 +49,9 @@ pub const CACHE_DURATION_LARGEST_COMMUNITY: Duration = DAY;
pub const MAX_COMMENT_DEPTH_LIMIT: usize = 50;
/// Doing DB transactions of bigger batches than this tend to cause seq scans.
pub const DB_BATCH_SIZE: i64 = 1000;
#[macro_export]
macro_rules! location_info {
() => {

View file

@ -4,11 +4,15 @@ use deser_hjson::from_str;
use std::{env, fs, sync::LazyLock};
use structs::{PictrsConfig, Settings};
use url::Url;
use urlencoding::encode;
pub mod structs;
static DEFAULT_CONFIG_FILE: &str = "config/config.hjson";
/// Some connection options to speed up queries
const CONNECTION_OPTIONS: [&str; 1] = ["geqo_threshold=12"];
#[allow(clippy::expect_used)]
pub static SETTINGS: LazyLock<Settings> = LazyLock::new(|| {
if env::var("LEMMY_INITIALIZE_WITH_DEFAULT_SETTINGS").is_ok() {
@ -84,6 +88,30 @@ impl Settings {
.clone()
.ok_or_else(|| anyhow!("images_disabled").into())
}
/// Sets a few additional config options necessary for starting lemmy
pub fn get_database_url_with_options(&self) -> LemmyResult<String> {
let mut url = Url::parse(&self.get_database_url())?;
// Set `lemmy.protocol_and_hostname` so triggers can use it
let lemmy_protocol_and_hostname_option =
"lemmy.protocol_and_hostname=".to_owned() + &self.get_protocol_and_hostname();
let mut options = CONNECTION_OPTIONS.to_vec();
options.push(&lemmy_protocol_and_hostname_option);
// Create the connection uri portion
let options_segments = options
.iter()
// The equal signs need to be encoded, since the url set_query doesn't do them,
// and postgres requires them to be %3D
// https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
.map(|o| format!("-c {}", encode(o)))
.collect::<Vec<String>>()
.join(" ");
url.set_query(Some(&format!("options={options_segments}")));
Ok(url.into())
}
}
#[allow(clippy::expect_used)]
/// Necessary to avoid URL expect failures

View file

@ -18,8 +18,18 @@ effective_io_concurrency = 200
work_mem = 3932kB
huge_pages = try
min_wal_size = 1GB
max_wal_size = 4GB
max_wal_size = 8GB
max_worker_processes = 16
max_parallel_workers_per_gather = 4
max_parallel_workers = 16
max_parallel_maintenance_workers = 4
# Listen address
listen_addresses = '*'
# Logging
session_preload_libraries = auto_explain
auto_explain.log_min_duration = 5ms
auto_explain.log_analyze = true
auto_explain.log_triggers = true
track_activity_query_size = 1048576

View file

@ -88,20 +88,7 @@ services:
# You can use this technique to add them here
# https://stackoverflow.com/a/30850095/1655478
hostname: postgres
command:
[
"postgres",
"-c",
"session_preload_libraries=auto_explain",
"-c",
"auto_explain.log_min_duration=5ms",
"-c",
"auto_explain.log_analyze=true",
"-c",
"auto_explain.log_triggers=true",
"-c",
"track_activity_query_size=1048576",
]
command: postgres -c config_file=/etc/postgresql.conf
ports:
# use a different port so it doesn't conflict with potential postgres db running on the host
- "5433:5432"

View file

@ -86,7 +86,7 @@ FROM
WHERE
blocked IS NOT NULL;
CREATE TABLE person_post_aggregates (
CREATE TABLE IF NOT EXISTS person_post_aggregates (
person_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
post_id int REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
read_comments bigint DEFAULT 0 NOT NULL,
@ -122,7 +122,7 @@ FROM
WHERE
hidden IS NOT NULL;
CREATE TABLE post_like (
CREATE TABLE IF NOT EXISTS post_like (
post_id int REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
person_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
score smallint NOT NULL,
@ -174,7 +174,24 @@ WHERE followed IS NULL;
DELETE FROM post_actions
WHERE read IS NULL;
ALTER TABLE comment_actions RENAME TO comment_like;
CREATE TABLE IF NOT EXISTS comment_like (
comment_id int REFERENCES COMMENT ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
person_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
score smallint NOT NULL,
published timestamptz DEFAULT now() NOT NULL,
PRIMARY KEY (person_id, comment_id)
);
INSERT INTO comment_like (comment_id, person_id, score, published)
SELECT
comment_id,
person_id,
like_score,
liked
FROM
comment_actions
WHERE
liked IS NOT NULL;
ALTER TABLE community_actions RENAME TO community_follower;
@ -182,11 +199,22 @@ ALTER TABLE instance_actions RENAME TO instance_block;
ALTER TABLE person_actions RENAME TO person_follower;
ALTER TABLE post_actions RENAME TO post_read;
CREATE TABLE IF NOT EXISTS post_read (
post_id int REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
person_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
published timestamptz DEFAULT now() NOT NULL,
PRIMARY KEY (person_id, post_id)
);
ALTER TABLE comment_like RENAME COLUMN liked TO published;
ALTER TABLE comment_like RENAME COLUMN like_score TO score;
INSERT INTO post_read (post_id, person_id, published)
SELECT
post_id,
person_id,
read
FROM
post_actions
WHERE
read IS NOT NULL;
ALTER TABLE community_follower RENAME COLUMN followed TO published;
@ -204,15 +232,6 @@ ALTER TABLE person_follower RENAME COLUMN followed TO published;
ALTER TABLE person_follower RENAME COLUMN follow_pending TO pending;
ALTER TABLE post_read RENAME COLUMN read TO published;
ALTER TABLE comment_like
DROP CONSTRAINT comment_actions_check_liked,
ALTER COLUMN published SET NOT NULL,
ALTER COLUMN published SET DEFAULT now(),
ALTER COLUMN score SET NOT NULL,
DROP COLUMN saved;
ALTER TABLE community_follower
DROP CONSTRAINT community_actions_check_followed,
DROP CONSTRAINT community_actions_check_received_ban,
@ -235,27 +254,7 @@ ALTER TABLE person_follower
ALTER COLUMN pending SET NOT NULL,
DROP COLUMN blocked;
ALTER TABLE post_read
DROP CONSTRAINT post_actions_check_read_comments,
DROP CONSTRAINT post_actions_check_liked,
ALTER COLUMN published SET NOT NULL,
ALTER COLUMN published SET DEFAULT now(),
DROP COLUMN read_comments,
DROP COLUMN read_comments_amount,
DROP COLUMN saved,
DROP COLUMN liked,
DROP COLUMN like_score,
DROP COLUMN hidden;
-- Rename associated stuff
ALTER INDEX comment_actions_pkey RENAME TO comment_like_pkey;
ALTER INDEX idx_comment_actions_comment RENAME TO idx_comment_like_comment;
ALTER TABLE comment_like RENAME CONSTRAINT comment_actions_comment_id_fkey TO comment_like_comment_id_fkey;
ALTER TABLE comment_like RENAME CONSTRAINT comment_actions_person_id_fkey TO comment_like_person_id_fkey;
ALTER INDEX community_actions_pkey RENAME TO community_follower_pkey;
ALTER INDEX idx_community_actions_community RENAME TO idx_community_follower_community;
@ -278,12 +277,6 @@ ALTER TABLE person_follower RENAME CONSTRAINT person_actions_target_id_fkey TO p
ALTER TABLE person_follower RENAME CONSTRAINT person_actions_person_id_fkey TO person_follower_follower_id_fkey;
ALTER INDEX post_actions_pkey RENAME TO post_read_pkey;
ALTER TABLE post_read RENAME CONSTRAINT post_actions_person_id_fkey TO post_read_person_id_fkey;
ALTER TABLE post_read RENAME CONSTRAINT post_actions_post_id_fkey TO post_read_post_id_fkey;
-- Rename idx_community_actions_followed and remove filter
CREATE INDEX idx_community_follower_published ON community_follower (published);
@ -304,17 +297,21 @@ CREATE INDEX idx_person_block_person ON person_block (person_id);
CREATE INDEX idx_person_block_target ON person_block (target_id);
CREATE INDEX idx_person_post_aggregates_person ON person_post_aggregates (person_id);
CREATE INDEX IF NOT EXISTS idx_person_post_aggregates_person ON person_post_aggregates (person_id);
CREATE INDEX idx_person_post_aggregates_post ON person_post_aggregates (post_id);
CREATE INDEX IF NOT EXISTS idx_person_post_aggregates_post ON person_post_aggregates (post_id);
CREATE INDEX idx_post_like_post ON post_like (post_id);
CREATE INDEX IF NOT EXISTS idx_post_like_post ON post_like (post_id);
CREATE INDEX idx_comment_like_comment ON comment_like (comment_id);
DROP INDEX idx_person_actions_person, idx_person_actions_target, idx_post_actions_person, idx_post_actions_post;
-- Drop `NOT NULL` indexes of columns that still exist
DROP INDEX idx_comment_actions_liked_not_null, idx_community_actions_followed_not_null, idx_person_actions_followed_not_null, idx_post_actions_read_not_null, idx_instance_actions_blocked_not_null;
DROP INDEX idx_comment_actions_liked_not_null, idx_community_actions_followed_not_null, idx_person_actions_followed_not_null, idx_post_actions_read_not_null, idx_instance_actions_blocked_not_null, idx_comment_actions_person, idx_community_actions_person, idx_instance_actions_instance, idx_instance_actions_person;
-- Drop statistics of columns that still exist
DROP statistics comment_actions_liked_stat, community_actions_followed_stat, person_actions_followed_stat;
DROP TABLE comment_actions, post_actions;

View file

@ -1,352 +1,55 @@
-- For each new actions table, transform the table previously used for the most common action type
-- into the new actions table, which should only change the table's metadata instead of rewriting the
-- rows
ALTER TABLE comment_like RENAME TO comment_actions;
ALTER TABLE community_follower RENAME TO community_actions;
ALTER TABLE instance_block RENAME TO instance_actions;
ALTER TABLE person_follower RENAME TO person_actions;
ALTER TABLE post_read RENAME TO post_actions;
ALTER TABLE comment_actions RENAME COLUMN published TO liked;
ALTER TABLE comment_actions RENAME COLUMN score TO like_score;
ALTER TABLE community_actions RENAME COLUMN published TO followed;
ALTER TABLE community_actions RENAME COLUMN state TO follow_state;
ALTER TABLE community_actions RENAME COLUMN approver_id TO follow_approver_id;
ALTER TABLE instance_actions RENAME COLUMN published TO blocked;
ALTER TABLE person_actions RENAME COLUMN person_id TO target_id;
ALTER TABLE person_actions RENAME COLUMN follower_id TO person_id;
ALTER TABLE person_actions RENAME COLUMN published TO followed;
ALTER TABLE person_actions RENAME COLUMN pending TO follow_pending;
ALTER TABLE post_actions RENAME COLUMN published TO read;
-- Mark all constraints of affected tables as deferrable to speed up migration
ALTER TABLE community_actions
ALTER CONSTRAINT community_follower_community_id_fkey DEFERRABLE;
ALTER TABLE community_actions
ALTER CONSTRAINT community_follower_approver_id_fkey DEFERRABLE;
ALTER TABLE community_actions
ALTER CONSTRAINT community_follower_person_id_fkey DEFERRABLE;
ALTER TABLE comment_actions
ALTER CONSTRAINT comment_like_comment_id_fkey DEFERRABLE;
ALTER TABLE comment_actions
ALTER CONSTRAINT comment_like_person_id_fkey DEFERRABLE;
ALTER TABLE instance_actions
ALTER CONSTRAINT instance_block_instance_id_fkey DEFERRABLE;
ALTER TABLE instance_actions
ALTER CONSTRAINT instance_block_person_id_fkey DEFERRABLE;
ALTER TABLE person_actions
ALTER CONSTRAINT person_follower_follower_id_fkey DEFERRABLE;
ALTER TABLE person_actions
ALTER CONSTRAINT person_follower_person_id_fkey DEFERRABLE;
ALTER TABLE post_actions
ALTER CONSTRAINT post_read_person_id_fkey DEFERRABLE;
ALTER TABLE post_actions
ALTER CONSTRAINT post_read_post_id_fkey DEFERRABLE;
ALTER TABLE comment_actions
ALTER COLUMN liked DROP NOT NULL,
ALTER COLUMN liked DROP DEFAULT,
ALTER COLUMN like_score DROP NOT NULL,
ADD COLUMN saved timestamptz;
ALTER TABLE community_actions
ALTER COLUMN followed DROP NOT NULL,
ALTER COLUMN followed DROP DEFAULT,
ALTER COLUMN follow_state DROP NOT NULL,
ADD COLUMN blocked timestamptz,
ADD COLUMN became_moderator timestamptz,
ADD COLUMN received_ban timestamptz,
ADD COLUMN ban_expires timestamptz;
ALTER TABLE instance_actions
ALTER COLUMN blocked DROP NOT NULL,
ALTER COLUMN blocked DROP DEFAULT;
ALTER TABLE person_actions
ALTER COLUMN followed DROP NOT NULL,
ALTER COLUMN followed DROP DEFAULT,
ALTER COLUMN follow_pending DROP NOT NULL,
ADD COLUMN blocked timestamptz;
ALTER TABLE post_actions
ALTER COLUMN read DROP NOT NULL,
ALTER COLUMN read DROP DEFAULT,
ADD COLUMN read_comments timestamptz,
ADD COLUMN read_comments_amount bigint,
ADD COLUMN saved timestamptz,
ADD COLUMN liked timestamptz,
ADD COLUMN like_score smallint,
ADD COLUMN hidden timestamptz;
-- Add actions from other old tables to the new tables
INSERT INTO comment_actions (person_id, comment_id, saved)
-- Consolidates all the old tables like post_read, post_like, into post_actions, to reduce joins and increase performance.
-- This creates the tables:
-- post_actions, comment_actions, community_actions, instance_actions, and person_actions.
--
-- comment_actions
CREATE TABLE comment_actions AS
SELECT
person_id,
comment_id,
published
FROM
comment_saved
ON CONFLICT (person_id,
comment_id)
DO UPDATE SET
saved = excluded.saved;
INSERT INTO person_actions (person_id, target_id, blocked)
SELECT
person_id,
target_id,
published
FROM
person_block
ON CONFLICT (person_id,
target_id)
DO UPDATE SET
blocked = excluded.blocked;
UPDATE
community_actions AS a
SET
blocked = (
SELECT
published
FROM
community_block AS b
WHERE (b.person_id, b.community_id) = (a.person_id, a.community_id)),
became_moderator = (
cast(max(like_score) AS smallint) AS like_score,
max(liked) AS liked,
max(saved) AS saved
FROM (
SELECT
person_id,
comment_id,
score AS like_score,
published AS liked,
NULL::timestamptz AS saved
FROM
comment_like
UNION ALL
SELECT
person_id,
comment_id,
NULL::int,
NULL::timestamptz,
published
FROM
community_moderator AS b
WHERE (b.person_id, b.community_id) = (a.person_id, a.community_id)),
(received_ban,
ban_expires) = (
SELECT
published,
expires
FROM
community_person_ban AS b
WHERE (b.person_id, b.community_id) = (a.person_id, a.community_id));
INSERT INTO community_actions (person_id, community_id, received_ban, ban_expires)
SELECT
comment_saved)
GROUP BY
person_id,
community_id,
published,
expires
FROM
community_person_ban AS b
WHERE
NOT EXISTS (
SELECT
FROM
community_actions AS a
WHERE (a.person_id, a.community_id) = (b.person_id, b.community_id));
comment_id;
INSERT INTO community_actions (person_id, community_id, blocked)
SELECT
person_id,
community_id,
published
FROM
community_block
ON CONFLICT (person_id,
community_id)
DO UPDATE SET
blocked = excluded.blocked
WHERE
community_actions.blocked IS NULL;
-- Drop the tables
DROP TABLE comment_saved, comment_like;
INSERT INTO community_actions (person_id, community_id, became_moderator)
SELECT
person_id,
community_id,
published
FROM
community_moderator
ON CONFLICT (person_id,
community_id)
DO UPDATE SET
became_moderator = excluded.became_moderator
WHERE
community_actions.became_moderator IS NULL;
UPDATE
post_actions AS a
SET
(read_comments,
read_comments_amount) = (
SELECT
published,
read_comments
FROM
person_post_aggregates AS b
WHERE (b.person_id, b.post_id) = (a.person_id, a.post_id)),
hidden = (
SELECT
published
FROM
post_hide AS b
WHERE (b.person_id, b.post_id) = (a.person_id, a.post_id)),
(liked,
like_score) = (
SELECT
published,
score
FROM
post_like AS b
WHERE (b.person_id, b.post_id) = (a.person_id, a.post_id)),
saved = (
SELECT
published
FROM
post_saved AS b
WHERE (b.person_id, b.post_id) = (a.person_id, a.post_id));
INSERT INTO post_actions (person_id, post_id, liked, like_score)
SELECT
person_id,
post_id,
published,
score
FROM
post_like AS b
WHERE
NOT EXISTS (
SELECT
FROM
post_actions AS a
WHERE (a.person_id, a.post_id) = (b.person_id, b.post_id));
INSERT INTO post_actions (person_id, post_id, read_comments, read_comments_amount)
SELECT
person_id,
post_id,
published,
read_comments
FROM
person_post_aggregates
ON CONFLICT (person_id,
post_id)
DO UPDATE SET
read_comments = excluded.read_comments,
read_comments_amount = excluded.read_comments_amount
WHERE
post_actions.read_comments IS NULL;
INSERT INTO post_actions (person_id, post_id, saved)
SELECT
person_id,
post_id,
published
FROM
post_saved
ON CONFLICT (person_id,
post_id)
DO UPDATE SET
saved = excluded.saved
WHERE
post_actions.saved IS NULL;
INSERT INTO post_actions (person_id, post_id, hidden)
SELECT
person_id,
post_id,
published
FROM
post_hide
ON CONFLICT (person_id,
post_id)
DO UPDATE SET
hidden = excluded.hidden
WHERE
post_actions.hidden IS NULL;
-- Drop old tables
DROP TABLE comment_saved, community_block, community_moderator, community_person_ban, person_block, person_post_aggregates, post_hide, post_like, post_saved;
-- Rename associated stuff
ALTER INDEX comment_like_pkey RENAME TO comment_actions_pkey;
ALTER INDEX idx_comment_like_comment RENAME TO idx_comment_actions_comment;
ALTER TABLE comment_actions RENAME CONSTRAINT comment_like_comment_id_fkey TO comment_actions_comment_id_fkey;
ALTER TABLE comment_actions RENAME CONSTRAINT comment_like_person_id_fkey TO comment_actions_person_id_fkey;
ALTER INDEX community_follower_pkey RENAME TO community_actions_pkey;
ALTER INDEX idx_community_follower_community RENAME TO idx_community_actions_community;
ALTER TABLE community_actions RENAME CONSTRAINT community_follower_community_id_fkey TO community_actions_community_id_fkey;
ALTER TABLE community_actions RENAME CONSTRAINT community_follower_person_id_fkey TO community_actions_person_id_fkey;
ALTER TABLE community_actions RENAME CONSTRAINT community_follower_approver_id_fkey TO community_actions_follow_approver_id_fkey;
ALTER INDEX instance_block_pkey RENAME TO instance_actions_pkey;
ALTER TABLE instance_actions RENAME CONSTRAINT instance_block_instance_id_fkey TO instance_actions_instance_id_fkey;
ALTER TABLE instance_actions RENAME CONSTRAINT instance_block_person_id_fkey TO instance_actions_person_id_fkey;
ALTER INDEX person_follower_pkey RENAME TO person_actions_pkey;
ALTER TABLE person_actions RENAME CONSTRAINT person_follower_person_id_fkey TO person_actions_target_id_fkey;
ALTER TABLE person_actions RENAME CONSTRAINT person_follower_follower_id_fkey TO person_actions_person_id_fkey;
ALTER INDEX post_read_pkey RENAME TO post_actions_pkey;
ALTER TABLE post_actions RENAME CONSTRAINT post_read_person_id_fkey TO post_actions_person_id_fkey;
ALTER TABLE post_actions RENAME CONSTRAINT post_read_post_id_fkey TO post_actions_post_id_fkey;
-- Rename idx_community_follower_published and add filter
CREATE INDEX idx_community_actions_followed ON community_actions (followed)
WHERE
followed IS NOT NULL;
DROP INDEX idx_community_follower_published;
-- Restore indexes of dropped tables
CREATE INDEX idx_community_actions_became_moderator ON community_actions (became_moderator)
WHERE
became_moderator IS NOT NULL;
CREATE INDEX idx_person_actions_person ON person_actions (person_id);
CREATE INDEX idx_person_actions_target ON person_actions (target_id);
CREATE INDEX idx_post_actions_person ON post_actions (person_id);
CREATE INDEX idx_post_actions_post ON post_actions (post_id);
-- Add the constraints
ALTER TABLE comment_actions
ALTER COLUMN person_id SET NOT NULL,
ALTER COLUMN comment_id SET NOT NULL,
ADD PRIMARY KEY (person_id, comment_id),
ADD CONSTRAINT comment_actions_person_id_fkey FOREIGN KEY (person_id) REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT comment_actions_comment_id_fkey FOREIGN KEY (comment_id) REFERENCES COMMENT ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT comment_actions_check_liked CHECK (((liked IS NULL) = (like_score IS NULL)));
-- Create new indexes, with `OR` being used to allow `IS NOT NULL` filters in queries to use either column in
-- a group (e.g. `liked IS NOT NULL` and `like_score IS NOT NULL` both work)
CREATE INDEX idx_comment_actions_person ON comment_actions (person_id);
CREATE INDEX idx_comment_actions_comment ON comment_actions (comment_id);
CREATE INDEX idx_comment_actions_liked_not_null ON comment_actions (person_id, comment_id)
WHERE
liked IS NOT NULL OR like_score IS NOT NULL;
@ -355,29 +58,106 @@ CREATE INDEX idx_comment_actions_saved_not_null ON comment_actions (person_id, c
WHERE
saved IS NOT NULL;
CREATE INDEX idx_community_actions_followed_not_null ON community_actions (person_id, community_id)
WHERE
followed IS NOT NULL OR follow_state IS NOT NULL;
-- Here's an SO link on merges, but this turned out to be slower than a
-- disabled triggers + disabled primary key + full union select + insert with group by
-- SO link on merges: https://stackoverflow.com/a/74066614/1655478
CREATE TABLE post_actions AS
SELECT
person_id,
post_id,
max(read) AS read,
max(read_comments) AS read_comments,
cast(max(read_comments_amount) AS int) AS read_comments_amount,
max(saved) AS saved,
max(liked) AS liked,
cast(max(like_score) AS smallint) AS like_score,
max(hidden) AS hidden
FROM (
SELECT
person_id,
post_id,
published AS read,
NULL::timestamptz AS read_comments,
NULL::int AS read_comments_amount,
NULL::timestamptz AS saved,
NULL::timestamptz AS liked,
NULL::int AS like_score,
NULL::timestamptz AS hidden
FROM
post_read
UNION ALL
SELECT
person_id,
post_id,
NULL::timestamptz,
published,
read_comments,
NULL::timestamptz,
NULL::timestamptz,
NULL::int,
NULL::timestamptz
FROM
person_post_aggregates
UNION ALL
SELECT
person_id,
post_id,
NULL::timestamptz,
NULL::timestamptz,
NULL::int,
published,
NULL::timestamptz,
NULL::int,
NULL::timestamptz
FROM
post_saved
UNION ALL
SELECT
person_id,
post_id,
NULL::timestamptz,
NULL::timestamptz,
NULL::int,
NULL::timestamptz,
published,
score,
NULL::timestamptz
FROM
post_like
UNION ALL
SELECT
person_id,
post_id,
NULL::timestamptz,
NULL::timestamptz,
NULL::int,
NULL::timestamptz,
NULL::timestamptz,
NULL::int,
published
FROM
post_hide)
GROUP BY
person_id,
post_id;
CREATE INDEX idx_community_actions_blocked_not_null ON community_actions (person_id, community_id)
WHERE
blocked IS NOT NULL;
-- Drop the tables
DROP TABLE post_read, person_post_aggregates, post_like, post_saved, post_hide;
CREATE INDEX idx_community_actions_became_moderator_not_null ON community_actions (person_id, community_id)
WHERE
became_moderator IS NOT NULL;
-- Add the constraints
ALTER TABLE post_actions
ALTER COLUMN person_id SET NOT NULL,
ALTER COLUMN post_id SET NOT NULL,
ADD PRIMARY KEY (person_id, post_id),
ADD CONSTRAINT post_actions_person_id_fkey FOREIGN KEY (person_id) REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT post_actions_post_id_fkey FOREIGN KEY (post_id) REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT post_actions_check_liked CHECK (((liked IS NULL) = (like_score IS NULL))),
ADD CONSTRAINT post_actions_check_read_comments CHECK (((read_comments IS NULL) = (read_comments_amount IS NULL)));
CREATE INDEX idx_community_actions_received_ban_not_null ON community_actions (person_id, community_id)
WHERE
received_ban IS NOT NULL;
-- Create indexes
CREATE INDEX idx_post_actions_person ON post_actions (person_id);
CREATE INDEX idx_person_actions_followed_not_null ON person_actions (person_id, target_id)
WHERE
followed IS NOT NULL OR follow_pending IS NOT NULL;
CREATE INDEX idx_person_actions_blocked_not_null ON person_actions (person_id, target_id)
WHERE
blocked IS NOT NULL;
CREATE INDEX idx_post_actions_post ON post_actions (post_id);
CREATE INDEX idx_post_actions_read_not_null ON post_actions (person_id, post_id)
WHERE
@ -399,12 +179,199 @@ CREATE INDEX idx_post_actions_hidden_not_null ON post_actions (person_id, post_i
WHERE
hidden IS NOT NULL;
-- community_actions
CREATE TABLE community_actions AS
SELECT
person_id,
community_id,
max(followed) AS followed,
max(follow_state) AS follow_state,
max(follow_approver_id) AS follow_approver_id,
max(blocked) AS blocked,
max(became_moderator) AS became_moderator,
max(received_ban) AS received_ban,
max(ban_expires) AS ban_expires
FROM (
SELECT
person_id,
community_id,
published AS followed,
state AS follow_state,
approver_id AS follow_approver_id,
NULL::timestamptz AS blocked,
NULL::timestamptz AS became_moderator,
NULL::timestamptz AS received_ban,
NULL::timestamptz AS ban_expires
FROM
community_follower
UNION ALL
SELECT
person_id,
community_id,
NULL::timestamptz,
NULL::community_follower_state,
NULL::int,
published,
NULL::timestamptz,
NULL::timestamptz,
NULL::timestamptz
FROM
community_block
UNION ALL
SELECT
person_id,
community_id,
NULL::timestamptz,
NULL::community_follower_state,
NULL::int,
NULL::timestamptz,
published,
NULL::timestamptz,
NULL::timestamptz
FROM
community_moderator
UNION ALL
SELECT
person_id,
community_id,
NULL::timestamptz,
NULL::community_follower_state,
NULL::int,
NULL::timestamptz,
NULL::timestamptz,
published,
expires
FROM
community_person_ban)
GROUP BY
person_id,
community_id;
-- Drop the old tables
DROP TABLE community_follower, community_block, community_moderator, community_person_ban;
-- Add the constraints
ALTER TABLE community_actions
ALTER COLUMN person_id SET NOT NULL,
ALTER COLUMN community_id SET NOT NULL,
ADD PRIMARY KEY (person_id, community_id),
ADD CONSTRAINT community_actions_person_id_fkey FOREIGN KEY (person_id) REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT community_actions_follow_approver_id_fkey FOREIGN KEY (follow_approver_id) REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT community_actions_community_id_fkey FOREIGN KEY (community_id) REFERENCES community ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT community_actions_check_followed CHECK ((((followed IS NULL) = (follow_state IS NULL)) AND (NOT ((followed IS NULL) AND (follow_approver_id IS NOT NULL))))),
ADD CONSTRAINT community_actions_check_received_ban CHECK ((NOT ((received_ban IS NULL) AND (ban_expires IS NOT NULL))));
-- Create indexes
CREATE INDEX idx_community_actions_person ON community_actions (person_id);
CREATE INDEX idx_community_actions_community ON community_actions (community_id);
CREATE INDEX idx_community_actions_followed ON community_actions (followed)
WHERE
followed IS NOT NULL;
CREATE INDEX idx_community_actions_followed_not_null ON community_actions (person_id, community_id)
WHERE
followed IS NOT NULL OR follow_state IS NOT NULL;
CREATE INDEX idx_community_actions_became_moderator ON community_actions (became_moderator)
WHERE
became_moderator IS NOT NULL;
CREATE INDEX idx_community_actions_became_moderator_not_null ON community_actions (person_id, community_id)
WHERE
became_moderator IS NOT NULL;
CREATE INDEX idx_community_actions_blocked_not_null ON community_actions (person_id, community_id)
WHERE
blocked IS NOT NULL;
CREATE INDEX idx_community_actions_received_ban_not_null ON community_actions (person_id, community_id)
WHERE
received_ban IS NOT NULL;
-- instance_actions
CREATE TABLE instance_actions AS
SELECT
person_id,
instance_id,
published AS blocked
FROM
instance_block;
DROP TABLE instance_block;
-- Add the constraints
ALTER TABLE instance_actions
ALTER COLUMN person_id SET NOT NULL,
ALTER COLUMN instance_id SET NOT NULL,
ADD PRIMARY KEY (person_id, instance_id),
ADD CONSTRAINT instance_actions_person_id_fkey FOREIGN KEY (person_id) REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT instance_actions_instance_id_fkey FOREIGN KEY (instance_id) REFERENCES instance ON UPDATE CASCADE ON DELETE CASCADE;
-- This index is currently redundant because instance_actions only has 1 action type, but inconsistency
-- with other tables would make it harder to do everything correctly when adding another action type
CREATE INDEX idx_instance_actions_person ON instance_actions (person_id);
CREATE INDEX idx_instance_actions_instance ON instance_actions (instance_id);
CREATE INDEX idx_instance_actions_blocked_not_null ON instance_actions (person_id, instance_id)
WHERE
blocked IS NOT NULL;
-- person_actions
CREATE TABLE person_actions AS
SELECT
person_id,
target_id,
max(followed) AS followed,
cast(max(follow_pending) AS boolean) AS follow_pending,
max(blocked) AS blocked
FROM (
SELECT
follower_id AS person_id,
person_id AS target_id,
published AS followed,
pending::int AS follow_pending,
NULL::timestamptz AS blocked
FROM
person_follower
UNION ALL
SELECT
person_id,
target_id,
NULL::timestamptz,
NULL::int,
published
FROM
person_block)
GROUP BY
person_id,
target_id;
-- add primary key, foreign keys, and not nulls
ALTER TABLE person_actions
ALTER COLUMN person_id SET NOT NULL,
ALTER COLUMN target_id SET NOT NULL,
ADD PRIMARY KEY (person_id, target_id),
ADD CONSTRAINT person_actions_person_id_fkey FOREIGN KEY (person_id) REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT person_actions_target_id_fkey FOREIGN KEY (target_id) REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT person_actions_check_followed CHECK (((followed IS NULL) = (follow_pending IS NULL)));
DROP TABLE person_block, person_follower;
CREATE INDEX idx_person_actions_person ON person_actions (person_id);
CREATE INDEX idx_person_actions_target ON person_actions (target_id);
CREATE INDEX idx_person_actions_followed_not_null ON person_actions (person_id, target_id)
WHERE
followed IS NOT NULL OR follow_pending IS NOT NULL;
CREATE INDEX idx_person_actions_blocked_not_null ON person_actions (person_id, target_id)
WHERE
blocked IS NOT NULL;
-- Create new statistics for more accurate estimations of how much of an index will be read (e.g. for
-- `(liked, like_score)`, the query planner might othewise assume that `(TRUE, FALSE)` and `(TRUE, TRUE)`
-- are equally likely when only `(TRUE, TRUE)` is possible, which would make it severely underestimate
@ -424,51 +391,3 @@ FROM post_actions;
CREATE statistics post_actions_liked_stat ON (liked IS NULL), (like_score IS NULL), (post_id IS NULL)
FROM post_actions;
ALTER TABLE comment_actions
ADD CONSTRAINT comment_actions_check_liked CHECK ((liked IS NULL) = (like_score IS NULL));
ALTER TABLE community_actions
ADD CONSTRAINT community_actions_check_followed CHECK ((followed IS NULL) = (follow_state IS NULL) AND NOT (followed IS NULL AND follow_approver_id IS NOT NULL)),
ADD CONSTRAINT community_actions_check_received_ban CHECK (NOT (received_ban IS NULL AND ban_expires IS NOT NULL));
ALTER TABLE person_actions
ADD CONSTRAINT person_actions_check_followed CHECK ((followed IS NULL) = (follow_pending IS NULL));
ALTER TABLE post_actions
ADD CONSTRAINT post_actions_check_read_comments CHECK ((read_comments IS NULL) = (read_comments_amount IS NULL)),
ADD CONSTRAINT post_actions_check_liked CHECK ((liked IS NULL) = (like_score IS NULL));
-- Remove deferrable to restore original db schema
ALTER TABLE community_actions
ALTER CONSTRAINT community_actions_community_id_fkey NOT DEFERRABLE;
ALTER TABLE community_actions
ALTER CONSTRAINT community_actions_follow_approver_id_fkey NOT DEFERRABLE;
ALTER TABLE community_actions
ALTER CONSTRAINT community_actions_person_id_fkey NOT DEFERRABLE;
ALTER TABLE comment_actions
ALTER CONSTRAINT comment_actions_comment_id_fkey NOT DEFERRABLE;
ALTER TABLE comment_actions
ALTER CONSTRAINT comment_actions_person_id_fkey NOT DEFERRABLE;
ALTER TABLE instance_actions
ALTER CONSTRAINT instance_actions_instance_id_fkey NOT DEFERRABLE;
ALTER TABLE instance_actions
ALTER CONSTRAINT instance_actions_person_id_fkey NOT DEFERRABLE;
ALTER TABLE person_actions
ALTER CONSTRAINT person_actions_person_id_fkey NOT DEFERRABLE;
ALTER TABLE person_actions
ALTER CONSTRAINT person_actions_target_id_fkey NOT DEFERRABLE;
ALTER TABLE post_actions
ALTER CONSTRAINT post_actions_person_id_fkey NOT DEFERRABLE;
ALTER TABLE post_actions
ALTER CONSTRAINT post_actions_post_id_fkey NOT DEFERRABLE;

View file

@ -4,9 +4,22 @@ ALTER TABLE post_aggregates
ADD COLUMN report_count smallint NOT NULL DEFAULT 0,
ADD COLUMN unresolved_report_count smallint NOT NULL DEFAULT 0;
ALTER TABLE comment_aggregates
ADD COLUMN report_count smallint NOT NULL DEFAULT 0,
ADD COLUMN unresolved_report_count smallint NOT NULL DEFAULT 0;
-- Disable the triggers temporarily
ALTER TABLE post_aggregates DISABLE TRIGGER ALL;
-- disable all table indexes
UPDATE
pg_index
SET
indisready = FALSE
WHERE
indrelid = (
SELECT
oid
FROM
pg_class
WHERE
relname = 'post_aggregates');
-- Update the historical counts
-- Posts
@ -43,6 +56,47 @@ FROM (
WHERE
a.post_id = cnt.post_id;
-- Re-enable triggers after upserts
ALTER TABLE post_aggregates ENABLE TRIGGER ALL;
-- Re-enable indexes
UPDATE
pg_index
SET
indisready = TRUE
WHERE
indrelid = (
SELECT
oid
FROM
pg_class
WHERE
relname = 'post_aggregates');
-- reindex
REINDEX TABLE post_aggregates;
ALTER TABLE comment_aggregates
ADD COLUMN report_count smallint NOT NULL DEFAULT 0,
ADD COLUMN unresolved_report_count smallint NOT NULL DEFAULT 0;
-- Disable the triggers temporarily
ALTER TABLE comment_aggregates DISABLE TRIGGER ALL;
-- disable all table indexes
UPDATE
pg_index
SET
indisready = FALSE
WHERE
indrelid = (
SELECT
oid
FROM
pg_class
WHERE
relname = 'comment_aggregates');
-- Comments
UPDATE
comment_aggregates AS a
@ -77,3 +131,23 @@ FROM (
WHERE
a.comment_id = cnt.comment_id;
-- Re-enable triggers after upserts
ALTER TABLE comment_aggregates ENABLE TRIGGER ALL;
-- Re-enable indexes
UPDATE
pg_index
SET
indisready = TRUE
WHERE
indrelid = (
SELECT
oid
FROM
pg_class
WHERE
relname = 'comment_aggregates');
-- reindex
REINDEX TABLE comment_aggregates;

View file

@ -1,4 +1,2 @@
DROP TABLE person_content_combined;
DROP TABLE person_saved_combined;
DROP TABLE person_content_combined, person_saved_combined;

View file

@ -1,21 +1,12 @@
-- Creates combined tables for
-- person_content: (comment, post)
-- person_saved: (comment, post)
CREATE TABLE person_content_combined (
id serial PRIMARY KEY,
published timestamptz NOT NULL,
post_id int UNIQUE REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
comment_id int UNIQUE REFERENCES COMMENT ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE
);
CREATE INDEX idx_person_content_combined_published ON person_content_combined (published DESC, id DESC);
-- Updating the history
INSERT INTO person_content_combined (published, post_id, comment_id)
-- TODO I have a feeling not including person_id on this table is a mistake, the join might not be fast.
CREATE TABLE person_content_combined AS
SELECT
published,
id,
NULL::int
id AS post_id,
NULL::int AS comment_id
FROM
post
UNION ALL
@ -26,51 +17,55 @@ SELECT
FROM
comment;
-- This one is special, because you use the saved date, not the ordinary published
CREATE TABLE person_saved_combined (
id serial PRIMARY KEY,
saved timestamptz NOT NULL,
person_id int NOT NULL REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
post_id int REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
comment_id int REFERENCES COMMENT ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE
);
-- Add the constraints
ALTER TABLE person_content_combined
ADD COLUMN id int PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
ALTER COLUMN published SET NOT NULL,
ADD CONSTRAINT person_content_combined_post_id_fkey FOREIGN KEY (post_id) REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT person_content_combined_comment_id_fkey FOREIGN KEY (comment_id) REFERENCES COMMENT ON UPDATE CASCADE ON DELETE CASCADE,
ADD UNIQUE (post_id),
ADD UNIQUE (comment_id),
ADD CONSTRAINT person_content_combined_check CHECK (num_nonnulls (post_id, comment_id) = 1);
-- Updating the history
INSERT INTO person_saved_combined (saved, person_id, post_id, comment_id)
-- This is for local_users only
CREATE TABLE person_saved_combined AS
SELECT
saved,
person_id,
post_id,
NULL::int
pa.saved AS saved,
pa.person_id AS person_id,
pa.post_id AS post_id,
NULL::int AS comment_id
FROM
post_actions
post_actions pa,
local_user lu
WHERE
saved IS NOT NULL
pa.person_id = lu.person_id
AND pa.saved IS NOT NULL
UNION ALL
SELECT
saved,
person_id,
ca.saved,
ca.person_id,
NULL::int,
comment_id
ca.comment_id
FROM
comment_actions
comment_actions ca,
local_user lu
WHERE
saved IS NOT NULL;
ca.person_id = lu.person_id
AND ca.saved IS NOT NULL;
-- Add the constraints
ALTER TABLE person_saved_combined
ADD COLUMN id int PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
ALTER COLUMN saved SET NOT NULL,
ALTER COLUMN person_id SET NOT NULL,
ADD CONSTRAINT person_saved_combined_person_id_fkey FOREIGN KEY (person_id) REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT person_saved_combined_post_id_fkey FOREIGN KEY (post_id) REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT person_saved_combined_comment_id_fkey FOREIGN KEY (comment_id) REFERENCES COMMENT ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT person_saved_combined_check CHECK (num_nonnulls (post_id, comment_id) = 1),
ADD UNIQUE (person_id, post_id),
ADD UNIQUE (person_id, comment_id);
CREATE INDEX idx_person_saved_combined_published ON person_saved_combined (saved DESC, id DESC);
CREATE INDEX idx_person_saved_combined ON person_saved_combined (person_id);
ALTER TABLE person_saved_combined
ALTER CONSTRAINT person_saved_combined_person_id_fkey NOT DEFERRABLE,
ALTER CONSTRAINT person_saved_combined_post_id_fkey NOT DEFERRABLE,
ALTER CONSTRAINT person_saved_combined_comment_id_fkey NOT DEFERRABLE,
ADD CONSTRAINT person_saved_combined_person_id_post_id_key UNIQUE (person_id, post_id),
ADD CONSTRAINT person_saved_combined_person_id_comment_id_key UNIQUE (person_id, comment_id),
ADD CONSTRAINT person_saved_combined_check CHECK (num_nonnulls (post_id, comment_id) = 1);
ALTER TABLE person_content_combined
ALTER CONSTRAINT person_content_combined_post_id_fkey NOT DEFERRABLE,
ALTER CONSTRAINT person_content_combined_comment_id_fkey NOT DEFERRABLE,
ADD CONSTRAINT person_content_combined_check CHECK (num_nonnulls (post_id, comment_id) = 1);

View file

@ -6,30 +6,22 @@ ALTER TABLE person_mention RENAME TO person_comment_mention;
-- Create the new post_mention table
CREATE TABLE person_post_mention (
id serial PRIMARY KEY,
recipient_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE NOT NULL,
post_id int REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE NOT NULL,
id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
recipient_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
post_id int REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
read boolean DEFAULT FALSE NOT NULL,
published timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE inbox_combined (
id serial PRIMARY KEY,
published timestamptz NOT NULL,
comment_reply_id int UNIQUE REFERENCES comment_reply ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
person_comment_mention_id int UNIQUE REFERENCES person_comment_mention ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
person_post_mention_id int UNIQUE REFERENCES person_post_mention ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
private_message_id int UNIQUE REFERENCES private_message ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE
published timestamptz NOT NULL DEFAULT now(),
UNIQUE (recipient_id, post_id)
);
-- Updating the history
INSERT INTO inbox_combined (published, comment_reply_id, person_comment_mention_id, person_post_mention_id, private_message_id)
CREATE TABLE inbox_combined AS
SELECT
published,
id,
NULL::int,
NULL::int,
NULL::int
id AS comment_reply_id,
NULL::int AS person_comment_mention_id,
NULL::int AS person_post_mention_id,
NULL::int AS private_message_id
FROM
comment_reply
UNION ALL
@ -60,20 +52,20 @@ SELECT
FROM
private_message;
ALTER TABLE inbox_combined
ADD COLUMN id int PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
ALTER COLUMN published SET NOT NULL,
ADD CONSTRAINT inbox_combined_comment_reply_id_fkey FOREIGN KEY (comment_reply_id) REFERENCES comment_reply ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT inbox_combined_person_comment_mention_id_fkey FOREIGN KEY (person_comment_mention_id) REFERENCES person_comment_mention ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT inbox_combined_person_post_mention_id_fkey FOREIGN KEY (person_post_mention_id) REFERENCES person_post_mention ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT inbox_combined_private_message_id_fkey FOREIGN KEY (private_message_id) REFERENCES private_message ON UPDATE CASCADE ON DELETE CASCADE,
ADD UNIQUE (comment_reply_id),
ADD UNIQUE (person_comment_mention_id),
ADD UNIQUE (person_post_mention_id),
ADD UNIQUE (private_message_id),
ADD CONSTRAINT inbox_combined_check CHECK (num_nonnulls (comment_reply_id, person_comment_mention_id, person_post_mention_id, private_message_id) = 1);
CREATE INDEX idx_inbox_combined_published ON inbox_combined (published DESC, id DESC);
CREATE INDEX idx_inbox_combined_published_asc ON inbox_combined (reverse_timestamp_sort (published) DESC, id DESC);
ALTER TABLE person_post_mention
ALTER CONSTRAINT person_post_mention_recipient_id_fkey NOT DEFERRABLE,
ALTER CONSTRAINT person_post_mention_post_id_fkey NOT DEFERRABLE,
ADD CONSTRAINT person_post_mention_unique UNIQUE (recipient_id, post_id);
-- Make sure only one of the columns is not null
ALTER TABLE inbox_combined
ADD CONSTRAINT inbox_combined_check CHECK (num_nonnulls (comment_reply_id, person_comment_mention_id, person_post_mention_id, private_message_id) = 1),
ALTER CONSTRAINT inbox_combined_comment_reply_id_fkey NOT DEFERRABLE,
ALTER CONSTRAINT inbox_combined_person_comment_mention_id_fkey NOT DEFERRABLE,
ALTER CONSTRAINT inbox_combined_person_post_mention_id_fkey NOT DEFERRABLE,
ALTER CONSTRAINT inbox_combined_private_message_id_fkey NOT DEFERRABLE;

View file

@ -1,20 +1,5 @@
-- Creates combined tables for
-- Search: (post, comment, community, person)
CREATE TABLE search_combined (
id serial PRIMARY KEY,
published timestamptz NOT NULL,
-- This is used for the top sort
-- For persons: its post score
-- For comments: score,
-- For posts: score,
-- For community: users active monthly
score bigint NOT NULL DEFAULT 0,
post_id int UNIQUE REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
comment_id int UNIQUE REFERENCES COMMENT ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
community_id int UNIQUE REFERENCES community ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
person_id int UNIQUE REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE
);
-- Add published to person_aggregates (it was missing for some reason)
ALTER TABLE person_aggregates
ADD COLUMN published timestamptz NOT NULL DEFAULT now();
@ -28,21 +13,26 @@ FROM
WHERE
pa.person_id = p.id;
-- score is used for the top sort
-- For persons: its post score
-- For comments: score,
-- For posts: score,
-- For community: users active monthly
-- Updating the history
INSERT INTO search_combined (published, score, post_id, comment_id, community_id, person_id)
CREATE TABLE search_combined AS
SELECT
published,
score,
score::int,
post_id,
NULL::int,
NULL::int,
NULL::int
NULL::int AS comment_id,
NULL::int AS community_id,
NULL::int AS person_id
FROM
post_aggregates
UNION ALL
SELECT
published,
score,
score::int,
NULL::int,
comment_id,
NULL::int,
@ -52,7 +42,7 @@ FROM
UNION ALL
SELECT
published,
users_active_month,
users_active_month::int,
NULL::int,
NULL::int,
community_id,
@ -62,7 +52,7 @@ FROM
UNION ALL
SELECT
published,
post_score,
post_score::int,
NULL::int,
NULL::int,
NULL::int,
@ -70,17 +60,25 @@ SELECT
FROM
person_aggregates;
-- Add the constraints
ALTER TABLE search_combined
ADD COLUMN id int PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
ALTER COLUMN published SET NOT NULL,
ALTER COLUMN score SET NOT NULL,
ALTER COLUMN score SET DEFAULT 0,
ADD CONSTRAINT search_combined_post_id_fkey FOREIGN KEY (post_id) REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT search_combined_comment_id_fkey FOREIGN KEY (comment_id) REFERENCES COMMENT ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT search_combined_community_id_fkey FOREIGN KEY (community_id) REFERENCES community ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT search_combined_person_id_fkey FOREIGN KEY (person_id) REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE,
ADD UNIQUE (post_id),
ADD UNIQUE (comment_id),
ADD UNIQUE (community_id),
ADD UNIQUE (person_id),
ADD CONSTRAINT search_combined_check CHECK (num_nonnulls (post_id, comment_id, community_id, person_id) = 1);
CREATE INDEX idx_search_combined_published ON search_combined (published DESC, id DESC);
CREATE INDEX idx_search_combined_published_asc ON search_combined (reverse_timestamp_sort (published) DESC, id DESC);
CREATE INDEX idx_search_combined_score ON search_combined (score DESC, id DESC);
-- Make sure only one of the columns is not null
ALTER TABLE search_combined
ADD CONSTRAINT search_combined_check CHECK (num_nonnulls (post_id, comment_id, community_id, person_id) = 1),
ALTER CONSTRAINT search_combined_post_id_fkey NOT DEFERRABLE,
ALTER CONSTRAINT search_combined_comment_id_fkey NOT DEFERRABLE,
ALTER CONSTRAINT search_combined_community_id_fkey NOT DEFERRABLE,
ALTER CONSTRAINT search_combined_person_id_fkey NOT DEFERRABLE;

View file

@ -4,6 +4,39 @@
-- Because of a postgres bug, you can't assign this to a new enum value,
-- unless you run an unsafe commit first. So just use active.
-- https://dba.stackexchange.com/questions/280371/postgres-unsafe-use-of-new-value-of-enum-type
--
-- Disable the triggers temporarily
ALTER TABLE local_user DISABLE TRIGGER ALL;
ALTER TABLE local_site DISABLE TRIGGER ALL;
-- disable all table indexes
UPDATE
pg_index
SET
indisready = FALSE
WHERE
indrelid = (
SELECT
oid
FROM
pg_class
WHERE
relname = 'local_user');
UPDATE
pg_index
SET
indisready = FALSE
WHERE
indrelid = (
SELECT
oid
FROM
pg_class
WHERE
relname = 'local_site');
UPDATE
local_user
SET
@ -67,3 +100,40 @@ ALTER TABLE local_user
ALTER TABLE local_site
ADD COLUMN default_post_time_range_seconds integer;
-- Re-enable the triggers
ALTER TABLE local_user ENABLE TRIGGER ALL;
ALTER TABLE local_site ENABLE TRIGGER ALL;
-- re-enable indexes
UPDATE
pg_index
SET
indisready = TRUE
WHERE
indrelid = (
SELECT
oid
FROM
pg_class
WHERE
relname = 'local_user');
UPDATE
pg_index
SET
indisready = TRUE
WHERE
indrelid = (
SELECT
oid
FROM
pg_class
WHERE
relname = 'local_site');
-- reindex
REINDEX TABLE local_user;
REINDEX TABLE local_site;

View file

@ -1,6 +1,6 @@
-- move comment_aggregates back into separate table
CREATE TABLE comment_aggregates (
comment_id int PRIMARY KEY NOT NULL REFERENCES COMMENT ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
CREATE TABLE IF NOT EXISTS comment_aggregates (
comment_id int PRIMARY KEY NOT NULL REFERENCES COMMENT ON UPDATE CASCADE ON DELETE CASCADE,
score bigint NOT NULL DEFAULT 0,
upvotes bigint NOT NULL DEFAULT 0,
downvotes bigint NOT NULL DEFAULT 0,
@ -25,7 +25,18 @@ SELECT
report_count,
unresolved_report_count
FROM
comment;
COMMENT
ON CONFLICT (comment_id)
DO UPDATE SET
score = excluded.score,
upvotes = excluded.upvotes,
downvotes = excluded.downvotes,
published = excluded.published,
child_count = excluded.child_count,
hot_rank = excluded.hot_rank,
controversy_rank = excluded.controversy_rank,
report_count = excluded.report_count,
unresolved_report_count = excluded.unresolved_report_count;
ALTER TABLE comment
DROP COLUMN score,
@ -37,24 +48,23 @@ ALTER TABLE comment
DROP COLUMN report_count,
DROP COLUMN unresolved_report_count;
SET CONSTRAINTS comment_aggregates_comment_id_fkey IMMEDIATE;
ALTER TABLE comment_aggregates
ALTER CONSTRAINT comment_aggregates_comment_id_fkey DEFERRABLE INITIALLY DEFERRED;
SET CONSTRAINTS comment_aggregates_comment_id_fkey DEFERRED;
CREATE INDEX IF NOT EXISTS idx_comment_aggregates_controversy ON comment_aggregates USING btree (controversy_rank DESC);
CREATE INDEX idx_comment_aggregates_controversy ON comment_aggregates USING btree (controversy_rank DESC);
CREATE INDEX IF NOT EXISTS idx_comment_aggregates_hot ON comment_aggregates USING btree (hot_rank DESC, score DESC);
CREATE INDEX idx_comment_aggregates_hot ON comment_aggregates USING btree (hot_rank DESC, score DESC);
CREATE INDEX idx_comment_aggregates_nonzero_hotrank ON comment_aggregates USING btree (published)
CREATE INDEX IF NOT EXISTS idx_comment_aggregates_nonzero_hotrank ON comment_aggregates USING btree (published)
WHERE (hot_rank <> (0)::double precision);
CREATE INDEX idx_comment_aggregates_published ON comment_aggregates USING btree (published DESC);
CREATE INDEX IF NOT EXISTS idx_comment_aggregates_published ON comment_aggregates USING btree (published DESC);
CREATE INDEX idx_comment_aggregates_score ON comment_aggregates USING btree (score DESC);
CREATE INDEX IF NOT EXISTS idx_comment_aggregates_score ON comment_aggregates USING btree (score DESC);
-- move comment_aggregates back into separate table
CREATE TABLE post_aggregates (
post_id int PRIMARY KEY NOT NULL REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
CREATE TABLE IF NOT EXISTS post_aggregates (
post_id int PRIMARY KEY NOT NULL REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE,
comments bigint NOT NULL DEFAULT 0,
score bigint NOT NULL DEFAULT 0,
upvotes bigint NOT NULL DEFAULT 0,
@ -66,10 +76,10 @@ CREATE TABLE post_aggregates (
featured_local boolean NOT NULL DEFAULT FALSE,
hot_rank double precision NOT NULL DEFAULT 0.0001,
hot_rank_active double precision NOT NULL DEFAULT 0.0001,
community_id integer NOT NULL REFERENCES community (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
creator_id integer NOT NULL REFERENCES person (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
community_id integer NOT NULL REFERENCES community (id) ON UPDATE CASCADE ON DELETE CASCADE,
creator_id integer NOT NULL REFERENCES person (id) ON UPDATE CASCADE ON DELETE CASCADE,
controversy_rank double precision NOT NULL DEFAULT 0,
instance_id integer NOT NULL REFERENCES instance (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
instance_id integer NOT NULL REFERENCES instance (id) ON UPDATE CASCADE ON DELETE CASCADE,
scaled_rank double precision NOT NULL DEFAULT 0.0001,
report_count smallint NOT NULL DEFAULT 0,
unresolved_report_count smallint NOT NULL DEFAULT 0
@ -97,7 +107,27 @@ SELECT
report_count,
unresolved_report_count
FROM
post;
post
ON CONFLICT (post_id)
DO UPDATE SET
comments = excluded.comments,
score = excluded.score,
upvotes = excluded.upvotes,
downvotes = excluded.downvotes,
published = excluded.published,
newest_comment_time_necro = excluded.newest_comment_time_necro,
newest_comment_time = excluded.newest_comment_time,
featured_community = excluded.featured_community,
featured_local = excluded.featured_local,
hot_rank = excluded.hot_rank,
hot_rank_active = excluded.hot_rank_active,
community_id = excluded.community_id,
creator_id = excluded.creator_id,
controversy_rank = excluded.controversy_rank,
instance_id = excluded.instance_id,
scaled_rank = excluded.scaled_rank,
report_count = excluded.report_count,
unresolved_report_count = excluded.unresolved_report_count;
ALTER TABLE post
DROP COLUMN comments,
@ -114,76 +144,78 @@ ALTER TABLE post
DROP COLUMN report_count,
DROP COLUMN unresolved_report_count;
SET CONSTRAINTS post_aggregates_community_id_fkey, post_aggregates_creator_id_fkey, post_aggregates_instance_id_fkey, post_aggregates_post_id_fkey IMMEDIATE;
ALTER TABLE post_aggregates
ALTER CONSTRAINT post_aggregates_community_id_fkey DEFERRABLE INITIALLY DEFERRED,
ALTER CONSTRAINT post_aggregates_creator_id_fkey DEFERRABLE INITIALLY DEFERRED,
ALTER CONSTRAINT post_aggregates_instance_id_fkey DEFERRABLE INITIALLY DEFERRED,
ALTER CONSTRAINT post_aggregates_post_id_fkey DEFERRABLE INITIALLY DEFERRED;
SET CONSTRAINTS post_aggregates_community_id_fkey, post_aggregates_creator_id_fkey, post_aggregates_instance_id_fkey, post_aggregates_post_id_fkey DEFERRED;
CREATE INDEX IF NOT EXISTS idx_post_aggregates_community_active ON post_aggregates USING btree (community_id, featured_local DESC, hot_rank_active DESC, published DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_community_active ON post_aggregates USING btree (community_id, featured_local DESC, hot_rank_active DESC, published DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_community_controversy ON post_aggregates USING btree (community_id, featured_local DESC, controversy_rank DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_community_controversy ON post_aggregates USING btree (community_id, featured_local DESC, controversy_rank DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_community_hot ON post_aggregates USING btree (community_id, featured_local DESC, hot_rank DESC, published DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_community_hot ON post_aggregates USING btree (community_id, featured_local DESC, hot_rank DESC, published DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_community_most_comments ON post_aggregates USING btree (community_id, featured_local DESC, comments DESC, published DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_community_most_comments ON post_aggregates USING btree (community_id, featured_local DESC, comments DESC, published DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_community_newest_comment_time ON post_aggregates USING btree (community_id, featured_local DESC, newest_comment_time DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_community_newest_comment_time ON post_aggregates USING btree (community_id, featured_local DESC, newest_comment_time DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_community_newest_comment_time_necro ON post_aggregates USING btree (community_id, featured_local DESC, newest_comment_time_necro DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_community_newest_comment_time_necro ON post_aggregates USING btree (community_id, featured_local DESC, newest_comment_time_necro DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_community_published ON post_aggregates USING btree (community_id, featured_local DESC, published DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_community_published ON post_aggregates USING btree (community_id, featured_local DESC, published DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_community_published_asc ON post_aggregates USING btree (community_id, featured_local DESC, reverse_timestamp_sort (published) DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_community_published_asc ON post_aggregates USING btree (community_id, featured_local DESC, reverse_timestamp_sort (published) DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_community_scaled ON post_aggregates USING btree (community_id, featured_local DESC, scaled_rank DESC, published DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_community_scaled ON post_aggregates USING btree (community_id, featured_local DESC, scaled_rank DESC, published DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_community_score ON post_aggregates USING btree (community_id, featured_local DESC, score DESC, published DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_community_score ON post_aggregates USING btree (community_id, featured_local DESC, score DESC, published DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_featured_community_active ON post_aggregates USING btree (community_id, featured_community DESC, hot_rank_active DESC, published DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_featured_community_active ON post_aggregates USING btree (community_id, featured_community DESC, hot_rank_active DESC, published DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_featured_community_controversy ON post_aggregates USING btree (community_id, featured_community DESC, controversy_rank DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_featured_community_controversy ON post_aggregates USING btree (community_id, featured_community DESC, controversy_rank DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_featured_community_hot ON post_aggregates USING btree (community_id, featured_community DESC, hot_rank DESC, published DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_featured_community_hot ON post_aggregates USING btree (community_id, featured_community DESC, hot_rank DESC, published DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_featured_community_most_comments ON post_aggregates USING btree (community_id, featured_community DESC, comments DESC, published DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_featured_community_most_comments ON post_aggregates USING btree (community_id, featured_community DESC, comments DESC, published DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_featured_community_newest_comment_time ON post_aggregates USING btree (community_id, featured_community DESC, newest_comment_time DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_featured_community_newest_comment_time ON post_aggregates USING btree (community_id, featured_community DESC, newest_comment_time DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_featured_community_newest_comment_time_necr ON post_aggregates USING btree (community_id, featured_community DESC, newest_comment_time_necro DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_featured_community_newest_comment_time_necr ON post_aggregates USING btree (community_id, featured_community DESC, newest_comment_time_necro DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_featured_community_published ON post_aggregates USING btree (community_id, featured_community DESC, published DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_featured_community_published ON post_aggregates USING btree (community_id, featured_community DESC, published DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_featured_community_published_asc ON post_aggregates USING btree (community_id, featured_community DESC, reverse_timestamp_sort (published) DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_featured_community_published_asc ON post_aggregates USING btree (community_id, featured_community DESC, reverse_timestamp_sort (published) DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_featured_community_scaled ON post_aggregates USING btree (community_id, featured_community DESC, scaled_rank DESC, published DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_featured_community_scaled ON post_aggregates USING btree (community_id, featured_community DESC, scaled_rank DESC, published DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_featured_community_score ON post_aggregates USING btree (community_id, featured_community DESC, score DESC, published DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_featured_community_score ON post_aggregates USING btree (community_id, featured_community DESC, score DESC, published DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_featured_local_active ON post_aggregates USING btree (featured_local DESC, hot_rank_active DESC, published DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_featured_local_active ON post_aggregates USING btree (featured_local DESC, hot_rank_active DESC, published DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_featured_local_controversy ON post_aggregates USING btree (featured_local DESC, controversy_rank DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_featured_local_controversy ON post_aggregates USING btree (featured_local DESC, controversy_rank DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_featured_local_hot ON post_aggregates USING btree (featured_local DESC, hot_rank DESC, published DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_featured_local_hot ON post_aggregates USING btree (featured_local DESC, hot_rank DESC, published DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_featured_local_most_comments ON post_aggregates USING btree (featured_local DESC, comments DESC, published DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_featured_local_most_comments ON post_aggregates USING btree (featured_local DESC, comments DESC, published DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_featured_local_newest_comment_time ON post_aggregates USING btree (featured_local DESC, newest_comment_time DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_featured_local_newest_comment_time ON post_aggregates USING btree (featured_local DESC, newest_comment_time DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_featured_local_newest_comment_time_necro ON post_aggregates USING btree (featured_local DESC, newest_comment_time_necro DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_featured_local_newest_comment_time_necro ON post_aggregates USING btree (featured_local DESC, newest_comment_time_necro DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_featured_local_published ON post_aggregates USING btree (featured_local DESC, published DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_featured_local_published ON post_aggregates USING btree (featured_local DESC, published DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_featured_local_published_asc ON post_aggregates USING btree (featured_local DESC, reverse_timestamp_sort (published) DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_featured_local_published_asc ON post_aggregates USING btree (featured_local DESC, reverse_timestamp_sort (published) DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_featured_local_scaled ON post_aggregates USING btree (featured_local DESC, scaled_rank DESC, published DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_featured_local_scaled ON post_aggregates USING btree (featured_local DESC, scaled_rank DESC, published DESC, post_id DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_featured_local_score ON post_aggregates USING btree (featured_local DESC, score DESC, published DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_featured_local_score ON post_aggregates USING btree (featured_local DESC, score DESC, published DESC, post_id DESC);
CREATE INDEX idx_post_aggregates_nonzero_hotrank ON post_aggregates USING btree (published DESC)
CREATE INDEX IF NOT EXISTS idx_post_aggregates_nonzero_hotrank ON post_aggregates USING btree (published DESC)
WHERE ((hot_rank <> (0)::double precision) OR (hot_rank_active <> (0)::double precision));
CREATE INDEX idx_post_aggregates_published ON post_aggregates USING btree (published DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_published ON post_aggregates USING btree (published DESC);
CREATE INDEX idx_post_aggregates_published_asc ON post_aggregates USING btree (reverse_timestamp_sort (published) DESC);
CREATE INDEX IF NOT EXISTS idx_post_aggregates_published_asc ON post_aggregates USING btree (reverse_timestamp_sort (published) DESC);
DROP INDEX idx_post_featured_community_published_asc;
@ -199,7 +231,7 @@ DROP INDEX idx_search_combined_score;
-- move community_aggregates back into separate table
CREATE TABLE community_aggregates (
community_id int PRIMARY KEY NOT NULL REFERENCES COMMunity ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
community_id int PRIMARY KEY NOT NULL REFERENCES community ON UPDATE CASCADE ON DELETE CASCADE,
subscribers bigint NOT NULL DEFAULT 0,
posts bigint NOT NULL DEFAULT 0,
comments bigint NOT NULL DEFAULT 0,
@ -246,12 +278,10 @@ ALTER TABLE community
DROP COLUMN subscribers_local,
DROP COLUMN report_count,
DROP COLUMN unresolved_report_count,
DROP COLUMN interactions_month,
ALTER CONSTRAINT community_instance_id_fkey NOT DEFERRABLE INITIALLY IMMEDIATE;
DROP COLUMN interactions_month;
SET CONSTRAINTS community_aggregates_community_id_fkey IMMEDIATE;
SET CONSTRAINTS community_aggregates_community_id_fkey DEFERRED;
ALTER TABLE community
ALTER CONSTRAINT community_instance_id_fkey NOT DEFERRABLE;
CREATE INDEX idx_community_aggregates_hot ON public.community_aggregates USING btree (hot_rank DESC);
@ -266,7 +296,7 @@ CREATE INDEX idx_community_aggregates_users_active_month ON public.community_agg
-- move person_aggregates back into separate table
CREATE TABLE person_aggregates (
person_id int PRIMARY KEY NOT NULL REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
person_id int PRIMARY KEY NOT NULL REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE,
post_count bigint NOT NULL DEFAULT 0,
post_score bigint NOT NULL DEFAULT 0,
comment_count bigint NOT NULL DEFAULT 0,
@ -291,9 +321,8 @@ ALTER TABLE person
DROP COLUMN comment_count,
DROP COLUMN comment_score;
SET CONSTRAINTS person_aggregates_person_id_fkey IMMEDIATE;
SET CONSTRAINTS person_aggregates_person_id_fkey DEFERRED;
ALTER TABLE person_aggregates
ALTER CONSTRAINT person_aggregates_person_id_fkey DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX idx_person_aggregates_comment_score ON public.person_aggregates USING btree (comment_score DESC);
@ -301,7 +330,7 @@ CREATE INDEX idx_person_aggregates_person ON public.person_aggregates USING btre
-- move site_aggregates back into separate table
CREATE TABLE site_aggregates (
site_id int PRIMARY KEY NOT NULL REFERENCES site ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
site_id int PRIMARY KEY NOT NULL REFERENCES site ON UPDATE CASCADE ON DELETE CASCADE,
users bigint NOT NULL DEFAULT 1,
posts bigint NOT NULL DEFAULT 0,
comments bigint NOT NULL DEFAULT 0,
@ -363,9 +392,11 @@ ALTER TABLE local_user
CREATE INDEX idx_search_combined_score ON public.search_combined USING btree (score DESC, id DESC);
SET CONSTRAINTS site_aggregates_site_id_fkey IMMEDIATE;
SET CONSTRAINTS site_aggregates_site_id_fkey DEFERRED;
ALTER TABLE site_aggregates
ALTER CONSTRAINT site_aggregates_site_id_fkey DEFERRABLE INITIALLY DEFERRED;
CREATE UNIQUE INDEX idx_site_aggregates_1_row_only ON public.site_aggregates USING btree ((TRUE));
ALTER TABLE community_aggregates
ALTER CONSTRAINT community_aggregates_community_id_fkey DEFERRABLE INITIALLY DEFERRED;

View file

@ -1,14 +1,31 @@
-- merge comment_aggregates into comment table
-- Merge comment_aggregates into comment table
ALTER TABLE comment
ADD COLUMN score bigint NOT NULL DEFAULT 0,
ADD COLUMN upvotes bigint NOT NULL DEFAULT 0,
ADD COLUMN downvotes bigint NOT NULL DEFAULT 0,
ADD COLUMN child_count integer NOT NULL DEFAULT 0,
ADD COLUMN score int NOT NULL DEFAULT 0,
ADD COLUMN upvotes int NOT NULL DEFAULT 0,
ADD COLUMN downvotes int NOT NULL DEFAULT 0,
ADD COLUMN child_count int NOT NULL DEFAULT 0,
ADD COLUMN hot_rank double precision NOT NULL DEFAULT 0.0001,
ADD COLUMN controversy_rank double precision NOT NULL DEFAULT 0,
ADD COLUMN report_count smallint NOT NULL DEFAULT 0,
ADD COLUMN unresolved_report_count smallint NOT NULL DEFAULT 0;
-- Disable the triggers temporarily
ALTER TABLE comment DISABLE TRIGGER ALL;
-- disable all table indexes
UPDATE
pg_index
SET
indisready = FALSE
WHERE
indrelid = (
SELECT
oid
FROM
pg_class
WHERE
relname = 'comment');
UPDATE
comment
SET
@ -25,6 +42,29 @@ FROM
WHERE
comment.id = ca.comment_id;
DROP TABLE comment_aggregates;
-- Re-enable triggers after upserts
ALTER TABLE comment ENABLE TRIGGER ALL;
-- Re-enable indexes
UPDATE
pg_index
SET
indisready = TRUE
WHERE
indrelid = (
SELECT
oid
FROM
pg_class
WHERE
relname = 'comment');
-- reindex
REINDEX TABLE comment;
-- 30s-2m each
CREATE INDEX idx_comment_controversy ON comment USING btree (controversy_rank DESC);
CREATE INDEX idx_comment_hot ON comment USING btree (hot_rank DESC, score DESC);
@ -37,20 +77,37 @@ CREATE INDEX idx_comment_score ON comment USING btree (score DESC);
-- merge post_aggregates into post table
ALTER TABLE post
ADD COLUMN comments bigint NOT NULL DEFAULT 0,
ADD COLUMN score bigint NOT NULL DEFAULT 0,
ADD COLUMN upvotes bigint NOT NULL DEFAULT 0,
ADD COLUMN downvotes bigint NOT NULL DEFAULT 0,
ADD COLUMN comments int NOT NULL DEFAULT 0,
ADD COLUMN score int NOT NULL DEFAULT 0,
ADD COLUMN upvotes int NOT NULL DEFAULT 0,
ADD COLUMN downvotes int NOT NULL DEFAULT 0,
ADD COLUMN newest_comment_time_necro timestamp with time zone NOT NULL DEFAULT now(),
ADD COLUMN newest_comment_time timestamp with time zone NOT NULL DEFAULT now(),
ADD COLUMN hot_rank double precision NOT NULL DEFAULT 0.0001,
ADD COLUMN hot_rank_active double precision NOT NULL DEFAULT 0.0001,
ADD COLUMN controversy_rank double precision NOT NULL DEFAULT 0,
ADD COLUMN instance_id int REFERENCES instance (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
ADD COLUMN instance_id int REFERENCES instance (id) ON UPDATE CASCADE ON DELETE CASCADE,
ADD COLUMN scaled_rank double precision NOT NULL DEFAULT 0.0001,
ADD COLUMN report_count smallint NOT NULL DEFAULT 0,
ADD COLUMN unresolved_report_count smallint NOT NULL DEFAULT 0;
-- Disable the triggers temporarily
ALTER TABLE post DISABLE TRIGGER ALL;
-- disable all table indexes
UPDATE
pg_index
SET
indisready = FALSE
WHERE
indrelid = (
SELECT
oid
FROM
pg_class
WHERE
relname = 'post');
UPDATE
post
SET
@ -72,9 +129,28 @@ FROM
WHERE
post.id = pa.post_id;
ALTER TABLE post
ALTER COLUMN instance_id SET NOT NULL,
ALTER CONSTRAINT post_instance_id_fkey NOT DEFERRABLE;
-- Delete that data
DROP TABLE post_aggregates;
-- Re-enable triggers after upserts
ALTER TABLE post ENABLE TRIGGER ALL;
-- Re-enable indexes
UPDATE
pg_index
SET
indisready = TRUE
WHERE
indrelid = (
SELECT
oid
FROM
pg_class
WHERE
relname = 'post');
-- reindex
REINDEX TABLE post;
CREATE INDEX idx_post_community_active ON post USING btree (community_id, featured_local DESC, hot_rank_active DESC, published DESC, id DESC);
@ -142,19 +218,35 @@ CREATE INDEX idx_post_published_asc ON post USING btree (reverse_timestamp_sort
-- merge community_aggregates into community table
ALTER TABLE community
ADD COLUMN subscribers bigint NOT NULL DEFAULT 0,
ADD COLUMN posts bigint NOT NULL DEFAULT 0,
ADD COLUMN comments bigint NOT NULL DEFAULT 0,
ADD COLUMN users_active_day bigint NOT NULL DEFAULT 0,
ADD COLUMN users_active_week bigint NOT NULL DEFAULT 0,
ADD COLUMN users_active_month bigint NOT NULL DEFAULT 0,
ADD COLUMN users_active_half_year bigint NOT NULL DEFAULT 0,
ADD COLUMN subscribers int NOT NULL DEFAULT 0,
ADD COLUMN posts int NOT NULL DEFAULT 0,
ADD COLUMN comments int NOT NULL DEFAULT 0,
ADD COLUMN users_active_day int NOT NULL DEFAULT 0,
ADD COLUMN users_active_week int NOT NULL DEFAULT 0,
ADD COLUMN users_active_month int NOT NULL DEFAULT 0,
ADD COLUMN users_active_half_year int NOT NULL DEFAULT 0,
ADD COLUMN hot_rank double precision NOT NULL DEFAULT 0.0001,
ADD COLUMN subscribers_local bigint NOT NULL DEFAULT 0,
ADD COLUMN subscribers_local int NOT NULL DEFAULT 0,
ADD COLUMN report_count smallint NOT NULL DEFAULT 0,
ADD COLUMN unresolved_report_count smallint NOT NULL DEFAULT 0,
ADD COLUMN interactions_month bigint NOT NULL DEFAULT 0,
ALTER CONSTRAINT community_instance_id_fkey DEFERRABLE INITIALLY DEFERRED;
ADD COLUMN interactions_month int NOT NULL DEFAULT 0;
-- Disable the triggers temporarily
ALTER TABLE community DISABLE TRIGGER ALL;
-- disable all table indexes
UPDATE
pg_index
SET
indisready = FALSE
WHERE
indrelid = (
SELECT
oid
FROM
pg_class
WHERE
relname = 'community');
UPDATE
community
@ -176,6 +268,28 @@ FROM
WHERE
community.id = ca.community_id;
DROP TABLE community_aggregates;
-- Re-enable triggers after upserts
ALTER TABLE community ENABLE TRIGGER ALL;
-- Re-enable indexes
UPDATE
pg_index
SET
indisready = TRUE
WHERE
indrelid = (
SELECT
oid
FROM
pg_class
WHERE
relname = 'community');
-- reindex
REINDEX TABLE community;
CREATE INDEX idx_community_hot ON public.community USING btree (hot_rank DESC);
CREATE INDEX idx_community_nonzero_hotrank ON public.community USING btree (published)
@ -187,10 +301,27 @@ CREATE INDEX idx_community_users_active_month ON public.community USING btree (u
-- merge person_aggregates into person table
ALTER TABLE person
ADD COLUMN post_count bigint NOT NULL DEFAULT 0,
ADD COLUMN post_score bigint NOT NULL DEFAULT 0,
ADD COLUMN comment_count bigint NOT NULL DEFAULT 0,
ADD COLUMN comment_score bigint NOT NULL DEFAULT 0;
ADD COLUMN post_count int NOT NULL DEFAULT 0,
ADD COLUMN post_score int NOT NULL DEFAULT 0,
ADD COLUMN comment_count int NOT NULL DEFAULT 0,
ADD COLUMN comment_score int NOT NULL DEFAULT 0;
-- Disable the triggers temporarily
ALTER TABLE person DISABLE TRIGGER ALL;
-- disable all table indexes
UPDATE
pg_index
SET
indisready = FALSE
WHERE
indrelid = (
SELECT
oid
FROM
pg_class
WHERE
relname = 'person');
UPDATE
person
@ -204,16 +335,55 @@ FROM
WHERE
person.id = pa.person_id;
-- merge site_aggregates into person table
DROP TABLE person_aggregates;
-- Re-enable triggers after upserts
ALTER TABLE person ENABLE TRIGGER ALL;
-- Re-enable indexes
UPDATE
pg_index
SET
indisready = TRUE
WHERE
indrelid = (
SELECT
oid
FROM
pg_class
WHERE
relname = 'person');
-- reindex
REINDEX TABLE person;
-- merge site_aggregates into local_site table
ALTER TABLE local_site
ADD COLUMN users bigint NOT NULL DEFAULT 1,
ADD COLUMN posts bigint NOT NULL DEFAULT 0,
ADD COLUMN comments bigint NOT NULL DEFAULT 0,
ADD COLUMN communities bigint NOT NULL DEFAULT 0,
ADD COLUMN users_active_day bigint NOT NULL DEFAULT 0,
ADD COLUMN users_active_week bigint NOT NULL DEFAULT 0,
ADD COLUMN users_active_month bigint NOT NULL DEFAULT 0,
ADD COLUMN users_active_half_year bigint NOT NULL DEFAULT 0;
ADD COLUMN users int NOT NULL DEFAULT 1,
ADD COLUMN posts int NOT NULL DEFAULT 0,
ADD COLUMN comments int NOT NULL DEFAULT 0,
ADD COLUMN communities int NOT NULL DEFAULT 0,
ADD COLUMN users_active_day int NOT NULL DEFAULT 0,
ADD COLUMN users_active_week int NOT NULL DEFAULT 0,
ADD COLUMN users_active_month int NOT NULL DEFAULT 0,
ADD COLUMN users_active_half_year int NOT NULL DEFAULT 0;
-- Disable the triggers temporarily
ALTER TABLE local_site DISABLE TRIGGER ALL;
-- disable all table indexes
UPDATE
pg_index
SET
indisready = FALSE
WHERE
indrelid = (
SELECT
oid
FROM
pg_class
WHERE
relname = 'local_site');
UPDATE
local_site
@ -231,6 +401,28 @@ FROM
WHERE
local_site.site_id = sa.site_id;
DROP TABLE site_aggregates;
-- Re-enable triggers after upserts
ALTER TABLE local_site ENABLE TRIGGER ALL;
-- Re-enable indexes
UPDATE
pg_index
SET
indisready = TRUE
WHERE
indrelid = (
SELECT
oid
FROM
pg_class
WHERE
relname = 'local_site');
-- reindex
REINDEX TABLE local_site;
-- merge local_user_vote_display_mode into local_user table
ALTER TABLE local_user
ADD COLUMN show_score boolean NOT NULL DEFAULT FALSE,
@ -238,6 +430,23 @@ ALTER TABLE local_user
ADD COLUMN show_downvotes boolean NOT NULL DEFAULT TRUE,
ADD COLUMN show_upvote_percentage boolean NOT NULL DEFAULT FALSE;
-- Disable the triggers temporarily
ALTER TABLE local_user DISABLE TRIGGER ALL;
-- disable all table indexes
UPDATE
pg_index
SET
indisready = FALSE
WHERE
indrelid = (
SELECT
oid
FROM
pg_class
WHERE
relname = 'local_user');
UPDATE
local_user
SET
@ -250,5 +459,25 @@ FROM
WHERE
local_user.id = v.local_user_id;
DROP TABLE comment_aggregates, post_aggregates, community_aggregates, person_aggregates, site_aggregates, local_user_vote_display_mode;
DROP TABLE local_user_vote_display_mode;
-- Re-enable triggers after upserts
ALTER TABLE local_user ENABLE TRIGGER ALL;
-- Re-enable indexes
UPDATE
pg_index
SET
indisready = TRUE
WHERE
indrelid = (
SELECT
oid
FROM
pg_class
WHERE
relname = 'local_user');
-- reindex
REINDEX TABLE local_user;

View file

@ -8,18 +8,18 @@ ALTER TABLE community
ADD COLUMN visibility_new community_visibility NOT NULL DEFAULT 'Public',
ADD COLUMN description_new varchar(150),
ADD COLUMN random_number_new smallint NOT NULL DEFAULT random_smallint (),
ADD COLUMN subscribers_new bigint NOT NULL DEFAULT 0,
ADD COLUMN posts_new bigint NOT NULL DEFAULT 0,
ADD COLUMN comments_new bigint NOT NULL DEFAULT 0,
ADD COLUMN users_active_day_new bigint NOT NULL DEFAULT 0,
ADD COLUMN users_active_week_new bigint NOT NULL DEFAULT 0,
ADD COLUMN users_active_month_new bigint NOT NULL DEFAULT 0,
ADD COLUMN users_active_half_year_new bigint NOT NULL DEFAULT 0,
ADD COLUMN subscribers_new int NOT NULL DEFAULT 0,
ADD COLUMN posts_new int NOT NULL DEFAULT 0,
ADD COLUMN comments_new int NOT NULL DEFAULT 0,
ADD COLUMN users_active_day_new int NOT NULL DEFAULT 0,
ADD COLUMN users_active_week_new int NOT NULL DEFAULT 0,
ADD COLUMN users_active_month_new int NOT NULL DEFAULT 0,
ADD COLUMN users_active_half_year_new int NOT NULL DEFAULT 0,
ADD COLUMN hot_rank_new double precision NOT NULL DEFAULT 0.0001,
ADD COLUMN subscribers_local_new bigint NOT NULL DEFAULT 0,
ADD COLUMN subscribers_local_new int NOT NULL DEFAULT 0,
ADD COLUMN report_count_new smallint NOT NULL DEFAULT 0,
ADD COLUMN unresolved_report_count_new smallint NOT NULL DEFAULT 0,
ADD COLUMN interactions_month_new bigint NOT NULL DEFAULT 0;
ADD COLUMN interactions_month_new int NOT NULL DEFAULT 0;
UPDATE
community
@ -62,10 +62,6 @@ SET
unresolved_report_count,
interactions_month);
SET CONSTRAINTS community_instance_id_fkey IMMEDIATE;
SET CONSTRAINTS community_instance_id_fkey DEFERRED;
ALTER TABLE community
ALTER COLUMN instance_id_new SET NOT NULL,
DROP COLUMN posting_restricted_to_mods,
@ -129,7 +125,7 @@ ALTER TABLE community RENAME COLUMN interactions_month_new TO interactions_month
ALTER TABLE community
ADD CONSTRAINT community_featured_url_key UNIQUE (featured_url),
ADD CONSTRAINT community_moderators_url_key UNIQUE (moderators_url),
ADD CONSTRAINT community_instance_id_fkey FOREIGN KEY (instance_id) REFERENCES instance (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
ADD CONSTRAINT community_instance_id_fkey FOREIGN KEY (instance_id) REFERENCES instance (id) ON UPDATE CASCADE ON DELETE CASCADE;
-- same changes as up.sql, but the other way round
UPDATE

View file

@ -18,10 +18,10 @@ ALTER TABLE person
ADD COLUMN bot_account_new boolean DEFAULT FALSE NOT NULL,
ADD COLUMN ban_expires timestamptz,
ADD COLUMN instance_id_new int,
ADD COLUMN post_count_new bigint DEFAULT 0 NOT NULL,
ADD COLUMN post_score_new bigint DEFAULT 0 NOT NULL,
ADD COLUMN comment_count_new bigint DEFAULT 0 NOT NULL,
ADD COLUMN comment_score_new bigint DEFAULT 0 NOT NULL;
ADD COLUMN post_count_new int DEFAULT 0 NOT NULL,
ADD COLUMN post_score_new int DEFAULT 0 NOT NULL,
ADD COLUMN comment_count_new int DEFAULT 0 NOT NULL,
ADD COLUMN comment_score_new int DEFAULT 0 NOT NULL;
UPDATE
person

View file

@ -22,10 +22,10 @@ ALTER TABLE post
ADD COLUMN url_content_type_new text,
ADD COLUMN alt_text_new text,
ADD COLUMN scheduled_publish_time_new timestamp with time zone,
ADD COLUMN comments_new bigint DEFAULT 0 NOT NULL,
ADD COLUMN score_new bigint DEFAULT 0 NOT NULL,
ADD COLUMN upvotes_new bigint DEFAULT 0 NOT NULL,
ADD COLUMN downvotes_new bigint DEFAULT 0 NOT NULL,
ADD COLUMN comments_new int DEFAULT 0 NOT NULL,
ADD COLUMN score_new int DEFAULT 0 NOT NULL,
ADD COLUMN upvotes_new int DEFAULT 0 NOT NULL,
ADD COLUMN downvotes_new int DEFAULT 0 NOT NULL,
ADD COLUMN newest_comment_time_necro_new timestamp with time zone DEFAULT now() NOT NULL,
ADD COLUMN newest_comment_time_new timestamp with time zone DEFAULT now() NOT NULL,
ADD COLUMN hot_rank_new double precision DEFAULT 0.0001 NOT NULL,
@ -337,6 +337,3 @@ ALTER TABLE post
ALTER TABLE post
ALTER COLUMN ap_id SET NOT NULL;
ALTER TABLE post
ALTER COLUMN instance_id SET NOT NULL;

View file

@ -1,28 +1,14 @@
-- Creates combined tables for
-- person_liked: (comment, post)
--
-- This one is special, because you use the liked date, not the ordinary published
CREATE TABLE person_liked_combined (
id serial PRIMARY KEY,
liked timestamptz NOT NULL,
like_score smallint NOT NULL,
person_id int NOT NULL REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
post_id int REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
comment_id int REFERENCES COMMENT ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE
);
CREATE INDEX idx_person_liked_combined_published ON person_liked_combined (liked DESC, id DESC);
CREATE INDEX idx_person_liked_combined ON person_liked_combined (person_id);
-- Updating the history
INSERT INTO person_liked_combined (liked, like_score, person_id, post_id, comment_id)
CREATE TABLE person_liked_combined AS
SELECT
pa.liked,
pa.like_score,
pa.person_id,
pa.post_id,
NULL::int
NULL::int AS comment_id
FROM
post_actions pa
INNER JOIN person p ON pa.person_id = p.id
@ -43,12 +29,17 @@ WHERE
liked IS NOT NULL
AND p.local = TRUE;
-- Make sure only one of the columns is not null
ALTER TABLE person_liked_combined
ADD CONSTRAINT person_liked_combined_person_id_comment_id_key UNIQUE (person_id, comment_id),
ADD CONSTRAINT person_liked_combined_person_id_post_id_key UNIQUE (person_id, post_id),
ADD CONSTRAINT person_liked_combined_check CHECK (num_nonnulls (post_id, comment_id) = 1),
ALTER CONSTRAINT person_liked_combined_person_id_fkey NOT DEFERRABLE,
ALTER CONSTRAINT person_liked_combined_post_id_fkey NOT DEFERRABLE,
ALTER CONSTRAINT person_liked_combined_comment_id_fkey NOT DEFERRABLE;
ADD COLUMN id int PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
ALTER COLUMN liked SET NOT NULL,
ALTER COLUMN like_score SET NOT NULL,
ALTER COLUMN person_id SET NOT NULL,
ADD CONSTRAINT person_liked_combined_person_id_fkey FOREIGN KEY (person_id) REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT person_liked_combined_post_id_fkey FOREIGN KEY (post_id) REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT person_liked_combined_comment_id_fkey FOREIGN KEY (comment_id) REFERENCES COMMENT ON UPDATE CASCADE ON DELETE CASCADE,
ADD UNIQUE (person_id, post_id),
ADD UNIQUE (person_id, comment_id),
ADD CONSTRAINT person_liked_combined_check CHECK (num_nonnulls (post_id, comment_id) = 1);
CREATE INDEX idx_person_liked_combined ON person_liked_combined (person_id);

View file

@ -27,14 +27,14 @@ ALTER TABLE local_site
ADD COLUMN comment_downvotes_new public.federation_mode_enum DEFAULT 'All'::public.federation_mode_enum NOT NULL,
ADD COLUMN default_post_time_range_seconds_new integer,
ADD COLUMN disallow_nsfw_content_new boolean DEFAULT FALSE NOT NULL,
ADD COLUMN users_new bigint DEFAULT 1 NOT NULL,
ADD COLUMN posts_new bigint DEFAULT 0 NOT NULL,
ADD COLUMN comments_new bigint DEFAULT 0 NOT NULL,
ADD COLUMN communities_new bigint DEFAULT 0 NOT NULL,
ADD COLUMN users_active_day_new bigint DEFAULT 0 NOT NULL,
ADD COLUMN users_active_week_new bigint DEFAULT 0 NOT NULL,
ADD COLUMN users_active_month_new bigint DEFAULT 0 NOT NULL,
ADD COLUMN users_active_half_year_new bigint DEFAULT 0 NOT NULL,
ADD COLUMN users_new int DEFAULT 1 NOT NULL,
ADD COLUMN posts_new int DEFAULT 0 NOT NULL,
ADD COLUMN comments_new int DEFAULT 0 NOT NULL,
ADD COLUMN communities_new int DEFAULT 0 NOT NULL,
ADD COLUMN users_active_day_new int DEFAULT 0 NOT NULL,
ADD COLUMN users_active_week_new int DEFAULT 0 NOT NULL,
ADD COLUMN users_active_month_new int DEFAULT 0 NOT NULL,
ADD COLUMN users_active_half_year_new int DEFAULT 0 NOT NULL,
ADD COLUMN disable_email_notifications_new boolean DEFAULT FALSE NOT NULL;
-- Update

View file

@ -6,6 +6,9 @@ ALTER TABLE person_actions
ALTER TABLE local_user
ADD COLUMN show_person_votes boolean NOT NULL DEFAULT TRUE;
-- Disable the triggers temporarily
ALTER TABLE person_actions DISABLE TRIGGER ALL;
-- Adding vote history
-- This union alls the comment and post actions tables,
-- inner joins to local_user for the above to filter out non-locals
@ -47,3 +50,6 @@ ON CONFLICT (person_id,
upvotes = excluded.upvotes,
downvotes = excluded.downvotes;
-- Re-enable the triggers
ALTER TABLE person_actions ENABLE TRIGGER ALL;

View file

@ -1,3 +0,0 @@
ALTER TABLE community
ALTER CONSTRAINT community_instance_id_fkey DEFERRABLE INITIALLY DEFERRED;

View file

@ -1,6 +0,0 @@
-- We should remove existing deferrable constraints, as they're potentially dangerous.
--
-- This is the only one I could find after doing a DB dump.
ALTER TABLE community
ALTER CONSTRAINT community_instance_id_fkey NOT DEFERRABLE;

View file

@ -1,5 +1,5 @@
CREATE TABLE person_post_mention (
id serial PRIMARY KEY,
id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
recipient_id int REFERENCES person (id) ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
post_id int REFERENCES post (id) ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
read bool NOT NULL DEFAULT FALSE,
@ -27,7 +27,7 @@ CREATE TABLE comment_reply (
);
CREATE TABLE inbox_combined (
id serial PRIMARY KEY,
id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
comment_reply_id int REFERENCES comment_reply (id) ON UPDATE CASCADE ON DELETE CASCADE UNIQUE,
person_comment_mention_id int REFERENCES person_comment_mention (id) ON UPDATE CASCADE ON DELETE CASCADE UNIQUE,
person_post_mention_id int REFERENCES person_post_mention (id) ON UPDATE CASCADE ON DELETE CASCADE UNIQUE,
@ -109,7 +109,7 @@ FROM
comment_reply;
ALTER TABLE ONLY person_post_mention
ADD CONSTRAINT person_post_mention_unique UNIQUE (recipient_id, post_id);
ADD CONSTRAINT person_post_mention_recipient_id_post_id_key UNIQUE (recipient_id, post_id);
ALTER TABLE inbox_combined
ADD CONSTRAINT inbox_combined_check CHECK (num_nonnulls (comment_reply_id, person_comment_mention_id, person_post_mention_id, private_message_id) = 1);

View file

@ -1,2 +1,2 @@
DROP INDEX idx_registration_application_admin, idx_admin_allow_instance_admin, idx_admin_block_instance_admin, idx_admin_purge_comment_admin, idx_admin_purge_community_admin, idx_admin_purge_person_admin, idx_admin_purge_post_admin, idx_mod_remove_comment_comment, idx_person_liked_combined_comment, idx_person_saved_combined_comment, idx_comment_report_creator, idx_community_report_creator, idx_post_report_creator, idx_private_message_creator, idx_private_message_report_creator, idx_admin_purge_post_community, idx_mod_add_community_community, idx_mod_ban_from_community_community, idx_mod_change_community_visibility_community, idx_mod_remove_community_community, idx_mod_transfer_community_community, idx_tag_community, idx_community_actions_follow_approver, idx_admin_allow_instance_instance, idx_admin_block_instance_instance, idx_community_instance, idx_instance_actions_instance, idx_mod_ban_instance, idx_multi_community_instance, idx_person_instance, idx_community_language_language, idx_local_user_language_language, idx_site_language_language, idx_email_verification_user, idx_oauth_account_user, idx_password_reset_request_user, idx_modlog_combined_mod_change_community_visibility_id, idx_mod_add_community_mod, idx_mod_add_mod, idx_mod_ban_from_community_mod, idx_mod_ban_mod, idx_mod_change_community_visibility_mod, idx_mod_feature_post_mod, idx_mod_lock_post_mod, idx_mod_remove_comment_mod, idx_mod_remove_community_mod, idx_mod_remove_post_mod, idx_mod_transfer_community_mod, idx_local_site_multi_comm_follower, idx_search_combined_multi_community, idx_mod_add_community_other_person, idx_mod_add_other_person, idx_mod_ban_from_community_other_person, idx_mod_other_person, idx_mod_transfer_community_other_person, idx_admin_purge_comment_post, idx_mod_feature_post_post, idx_mod_lock_post_post, idx_mod_remove_post_post, idx_person_liked_combined_post, idx_person_saved_combined_post, idx_private_message_recipient, idx_comment_report_resolver, idx_community_report_resolver, idx_post_report_resolver, idx_private_message_report_resolver, idx_local_site_suggested_communities, idx_post_tag_tag, idx_local_image_thumbnail_post;
DROP INDEX idx_registration_application_admin, idx_admin_allow_instance_admin, idx_admin_block_instance_admin, idx_admin_purge_comment_admin, idx_admin_purge_community_admin, idx_admin_purge_person_admin, idx_admin_purge_post_admin, idx_mod_remove_comment_comment, idx_person_liked_combined_comment, idx_person_saved_combined_comment, idx_comment_report_creator, idx_community_report_creator, idx_post_report_creator, idx_private_message_creator, idx_private_message_report_creator, idx_admin_purge_post_community, idx_mod_add_community_community, idx_mod_ban_from_community_community, idx_mod_change_community_visibility_community, idx_mod_remove_community_community, idx_mod_transfer_community_community, idx_tag_community, idx_community_actions_follow_approver, idx_admin_allow_instance_instance, idx_admin_block_instance_instance, idx_community_instance, idx_mod_ban_instance, idx_multi_community_instance, idx_person_instance, idx_community_language_language, idx_local_user_language_language, idx_site_language_language, idx_email_verification_user, idx_oauth_account_user, idx_password_reset_request_user, idx_modlog_combined_mod_change_community_visibility_id, idx_mod_add_community_mod, idx_mod_add_mod, idx_mod_ban_from_community_mod, idx_mod_ban_mod, idx_mod_change_community_visibility_mod, idx_mod_feature_post_mod, idx_mod_lock_post_mod, idx_mod_remove_comment_mod, idx_mod_remove_community_mod, idx_mod_remove_post_mod, idx_mod_transfer_community_mod, idx_local_site_multi_comm_follower, idx_search_combined_multi_community, idx_mod_add_community_other_person, idx_mod_add_other_person, idx_mod_ban_from_community_other_person, idx_mod_other_person, idx_mod_transfer_community_other_person, idx_admin_purge_comment_post, idx_mod_feature_post_post, idx_mod_lock_post_post, idx_mod_remove_post_post, idx_person_liked_combined_post, idx_person_saved_combined_post, idx_private_message_recipient, idx_comment_report_resolver, idx_community_report_resolver, idx_post_report_resolver, idx_private_message_report_resolver, idx_local_site_suggested_communities, idx_post_tag_tag, idx_local_image_thumbnail_post;

View file

@ -50,8 +50,6 @@ CREATE INDEX idx_admin_block_instance_instance ON admin_block_instance (instance
CREATE INDEX idx_community_instance ON community (instance_id);
CREATE INDEX idx_instance_actions_instance ON instance_actions (instance_id);
CREATE INDEX idx_mod_ban_instance ON mod_ban (instance_id);
CREATE INDEX idx_multi_community_instance ON multi_community (instance_id);

View file

@ -0,0 +1,56 @@
# post_read -> post_actions bulk upsert timings
## normal, 1 month: 491s
Insert on post_actions (cost=0.57..371215.69 rows=0 width=0) (actual time=169235.026..169235.026 rows=0 loops=1) Conflict Resolution: UPDATE Conflict Arbiter Indexes: post_actions_pkey Tuples Inserted: 5175253 Conflicting Tuples: 0 -> Index Scan using idx_post_read_published_desc on post_read (cost=0.57..371215.69 rows=5190811 width=58) (actual time=47.762..39310.551 rows=5175253 loops=1) Index Cond: (published > (CURRENT_DATE - '6 mons'::interval)) Planning Time: 0.234 ms Trigger for constraint post_actions_person_id_fkey: time=118828.666 calls=5175253 Trigger for constraint post_actions_post_id_fkey: time=203098.355 calls=5175253 JIT: Functions: 6 Options: Inlining false, Optimization false, Expressions true, Deforming true Timing: Generation 0.448 ms, Inlining 0.000 ms, Optimization 0.201 ms, Emission 44.721 ms, Total 45.369 ms Execution Time: 491991.365 ms (15 rows)
## disabled triggers, keep pkey, on conflict: 167s
Insert on post_actions (cost=0.57..371215.69 rows=0 width=0) (actual time=167261.176..167261.176 rows=0 loops=1) Conflict Resolution: UPDATE Conflict Arbiter Indexes: post_actions_pkey Tuples Inserted: 5175253 Conflicting Tuples: 0 -> Index Scan using idx_tmp_1 on post_read (cost=0.57..371215.69 rows=5190811 width=58) (actual time=5.604..59193.030 rows=5175253 loops=1) Index Cond: (published > (CURRENT_DATE - '6 mons'::interval)) Planning Time: 0.147 ms JIT: Functions: 6 Options: Inlining false, Optimization false, Expressions true, Deforming true Timing: Generation 0.490 ms, Inlining 0.000 ms, Optimization 0.197 ms, Emission 3.989 ms, Total 4.675 ms Execution Time: 167261.807 ms
## disabled triggers, with pkey, insert only: 91s
Insert on post_actions (cost=0.57..371215.69 rows=0 width=0) (actual time=91820.768..91820.769 rows=0 loops=1) -> Index Scan using idx_tmp_1 on post_read (cost=0.57..371215.69 rows=5190811 width=58) (actual time=5.482..40066.185 rows=5175253 loops=1) Index Cond: (published > (CURRENT_DATE - '6 mons'::interval)) Planning Time: 0.098 ms JIT: Functions: 5 Options: Inlining false, Optimization false, Expressions true, Deforming true Timing: Generation 0.490 ms, Inlining 0.000 ms, Optimization 0.208 ms, Emission 3.894 ms, Total 4.592 ms Execution Time: 91821.724 ms
## disabled triggers, no pkey, insert only: 57s
Insert on post_actions (cost=0.57..371215.69 rows=0 width=0) (actual time=56797.431..56797.432 rows=0 loops=1) -> Index Scan using idx_tmp_1 on post_read (cost=0.57..371215.69 rows=5190811 width=58) (actual time=4.827..27903.829 rows=5175253 loops=1) Index Cond: (published > (CURRENT_DATE - '6 mons'::interval)) Planning Time: 0.096 ms JIT: Functions: 5 Options: Inlining false, Optimization false, Expressions true, Deforming true Timing: Generation 0.390 ms, Inlining 0.000 ms, Optimization 0.232 ms, Emission 3.373 ms, Total 3.994 ms Execution Time: 56798.022 ms
## disabled triggers, merge instead of upsert: 77s
Merge on post_actions pa (cost=34.06..280379.97 rows=0 width=0) (actual time=76988.823..76988.825 rows=0 loops=1) Tuples: inserted=5175253 -> Hash Left Join (cost=34.06..280379.97 rows=1098137 width=28) (actual time=8.109..12202.884 rows=5175253 loops=1) Hash Cond: ((post_read.person_id = pa.person_id) AND (post_read.post_id = pa.post_id)) -> Index Scan using idx_tmp_1 on post_read (cost=0.56..274581.25 rows=1098137 width=22) (actual time=8.094..11432.132 rows=5175253 loops=1) Index Cond: (published > (CURRENT_DATE - '6 mons'::interval)) -> Hash (cost=19.40..19.40 rows=940 width=14) (actual time=0.003..0.004 rows=0 loops=1) Buckets: 1024 Batches: 1 Memory Usage: 8kB -> Seq Scan on post_actions pa (cost=0.00..19.40 rows=940 width=14) (actual time=0.003..0.003 rows=0 loops=1) Planning Time: 0.468 ms JIT: Functions: 17 Options: Inlining false, Optimization false, Expressions true, Deforming true Timing: Generation 0.897 ms, Inlining 0.000 ms, Optimization 0.399 ms, Emission 7.650 ms, Total 8.946 ms Execution Time: 76989.946 ms
## disabled triggers, merge, no pkey: 39s
Merge on post_actions pa (cost=297488.30..303957.64 rows=0 width=0) (actual time=39009.474..39009.477 rows=0 loops=1) Tuples: inserted=5175253 -> Hash Right Join (cost=297488.30..303957.64 rows=1098137 width=28) (actual time=3412.832..5353.677 rows=5175253 loops=1) Hash Cond: ((pa.person_id = post_read.person_id) AND (pa.post_id = post_read.post_id)) -> Seq Scan on post_actions pa (cost=0.00..19.40 rows=940 width=14) (actual time=0.004..0.005 rows=0 loops=1) -> Hash (cost=274581.25..274581.25 rows=1098137 width=22) (actual time=3412.178..3412.180 rows=5175253 loops=1) Buckets: 131072 (originally 131072) Batches: 64 (originally 16) Memory Usage: 7169kB -> Index Scan using idx_tmp_1 on post_read (cost=0.56..274581.25 rows=1098137 width=22) (actual time=8.495..2299.278 rows=5175253 loops=1) Index Cond: (published > (CURRENT_DATE - '6 mons'::interval)) Planning Time: 0.465 ms JIT: Functions: 17 Options: Inlining false, Optimization false, Expressions true, Deforming true Timing: Generation 0.988 ms, Inlining 0.000 ms, Optimization 0.350 ms, Emission 8.127 ms, Total 9.465 ms Execution Time: 39011.515 ms
## same as above, full table: 425s
Merge on post_actions pa (cost=1478580.50..1520165.83 rows=0 width=0) (actual time=425751.243..425751.245 rows=0 loops=1) Tuples: inserted=33519660 -> Hash Right Join (cost=1478580.50..1520165.83 rows=7091220 width=28) (actual time=72968.237..120866.662 rows=33519660 loops=1) Hash Cond: ((pa.person_id = pr.person_id) AND (pa.post_id = pr.post_id)) -> Seq Scan on post_actions pa (cost=0.00..19.40 rows=940 width=14) (actual time=0.004..0.004 rows=0 loops=1) -> Hash (cost=1330661.20..1330661.20 rows=7091220 width=22) (actual time=72967.590..72967.591 rows=33519660 loops=1) Buckets: 131072 (originally 131072) Batches: 256 (originally 64) Memory Usage: 7927kB -> Seq Scan on post_read pr (cost=0.00..1330661.20 rows=7091220 width=22) (actual time=103.545..51892.728 rows=33519660 loops=1) Planning Time: 0.393 ms JIT: Functions: 14 Options: Inlining true, Optimization true, Expressions true, Deforming true Timing: Generation 0.840 ms, Inlining 11.303 ms, Optimization 45.211 ms, Emission 40.003 ms, Total 97.357 ms Execution Time: 425753.438 ms
## disabled triggers, merge, with pkey, full table: 587s
Merge on post_actions pa (cost=19.47..1367909.58 rows=0 width=0) (actual time=587295.757..587295.759 rows=0 loops=1) Tuples: inserted=33519660 -> Hash Left Join (cost=19.47..1367909.58 rows=7091220 width=28) (actual time=77.291..46496.679 rows=33519660 loops=1) Hash Cond: ((pr.person_id = pa.person_id) AND (pr.post_id = pa.post_id)) -> Seq Scan on post_read pr (cost=0.00..1330661.20 rows=7091220 width=22) (actual time=77.266..41178.528 rows=33519660 loops=1) -> Hash (cost=19.40..19.40 rows=5 width=14) (actual time=0.006..0.007 rows=0 loops=1) Buckets: 1024 Batches: 1 Memory Usage: 8kB -> Seq Scan on post_actions pa (cost=0.00..19.40 rows=5 width=14) (actual time=0.006..0.006 rows=0 loops=1) Filter: (read IS NULL) Planning Time: 0.428 ms JIT: Functions: 16 Options: Inlining true, Optimization true, Expressions true, Deforming true Timing: Generation 0.922 ms, Inlining 6.324 ms, Optimization 37.862 ms, Emission 33.076 ms, Total 78.183 ms Execution Time: 587297.207 ms (15 rows)
## disabled triggers, merge, no pkey, full table: 359s
## disabled triggers, merge, no pkey, person_post_aggs after post_read: 1260s
## disabled triggers, no pkey, post_read + person_post_aggs union all with group by insert (no upsert or merge): 402s
### Merge example:
```sql
EXPLAIN ANALYZE MERGE INTO post_actions pa
USING post_read pr ON (pa.person_id = pr.person_id
AND pa.post_id = pr.post_id
)
WHEN MATCHED THEN
UPDATE SET
read = pr.published
WHEN NOT MATCHED THEN
INSERT (person_id, post_id, read)
VALUES (pr.person_id, pr.post_id, pr.published);
```
## comment aggregate bulk update: 3881s / 65m

View file

@ -155,7 +155,7 @@ pub async fn start_lemmy_server(args: CmdArgs) -> LemmyResult<()> {
options = options.limit(number);
}
lemmy_db_schema_setup::run(options, &SETTINGS.get_database_url())?;
lemmy_db_schema_setup::run(options, &SETTINGS.get_database_url_with_options()?)?;
#[cfg(debug_assertions)]
if all && subcommand == MigrationSubcommand::Run {