perf(api,frontend): index hot queries, paginate chats, fix realtime
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
"""hot path indexes
|
||||
|
||||
Revision ID: c1f6b3d84a92
|
||||
Revises: b9e4d1a70c26
|
||||
Create Date: 2026-08-06 12:00:00.000000
|
||||
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "c1f6b3d84a92"
|
||||
down_revision: str | None = "b9e4d1a70c26"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.execute(
|
||||
"CREATE INDEX ix_messages_chat_date ON messages "
|
||||
"(account_id, chat_id, date DESC, message_id DESC)"
|
||||
)
|
||||
op.execute("CREATE INDEX ix_avatars_owner ON avatars (account_id, owner_id)")
|
||||
op.execute("CREATE INDEX ix_media_message ON media (account_id, message_id)")
|
||||
op.execute(
|
||||
"CREATE INDEX ix_chat_history_chat_ts ON chat_history "
|
||||
"(account_id, chat_id, ts DESC)"
|
||||
)
|
||||
op.execute(
|
||||
"CREATE INDEX ix_read_receipts_chat ON read_receipts "
|
||||
"(account_id, chat_id, kind, message_id DESC)"
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.execute("DROP INDEX IF EXISTS ix_read_receipts_chat")
|
||||
op.execute("DROP INDEX IF EXISTS ix_chat_history_chat_ts")
|
||||
op.execute("DROP INDEX IF EXISTS ix_media_message")
|
||||
op.execute("DROP INDEX IF EXISTS ix_avatars_owner")
|
||||
op.execute("DROP INDEX IF EXISTS ix_messages_chat_date")
|
||||
@@ -0,0 +1,119 @@
|
||||
"""chat stats
|
||||
|
||||
Revision ID: d4a7e2b91f38
|
||||
Revises: c1f6b3d84a92
|
||||
Create Date: 2026-08-06 12:30:00.000000
|
||||
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "d4a7e2b91f38"
|
||||
down_revision: str | None = "c1f6b3d84a92"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
_APPLY = """
|
||||
CREATE FUNCTION chat_stats_apply() RETURNS trigger LANGUAGE plpgsql AS $$
|
||||
BEGIN
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
INSERT INTO chat_stats AS cs (account_id, chat_id, message_count,
|
||||
last_date, last_message_id,
|
||||
last_text, last_sender_id)
|
||||
VALUES (NEW.account_id, NEW.chat_id, 1,
|
||||
CASE WHEN NEW.date <= now() + interval '1 day'
|
||||
THEN NEW.date END,
|
||||
CASE WHEN NEW.date <= now() + interval '1 day'
|
||||
THEN NEW.message_id END,
|
||||
NEW.text, NEW.sender_id)
|
||||
ON CONFLICT (account_id, chat_id) DO UPDATE SET
|
||||
message_count = cs.message_count + 1,
|
||||
last_date = CASE WHEN chat_stats_newer(cs, EXCLUDED)
|
||||
THEN EXCLUDED.last_date ELSE cs.last_date END,
|
||||
last_message_id = CASE WHEN chat_stats_newer(cs, EXCLUDED)
|
||||
THEN EXCLUDED.last_message_id
|
||||
ELSE cs.last_message_id END,
|
||||
last_text = CASE WHEN chat_stats_newer(cs, EXCLUDED)
|
||||
THEN EXCLUDED.last_text ELSE cs.last_text END,
|
||||
last_sender_id = CASE WHEN chat_stats_newer(cs, EXCLUDED)
|
||||
THEN EXCLUDED.last_sender_id
|
||||
ELSE cs.last_sender_id END;
|
||||
ELSE
|
||||
UPDATE chat_stats
|
||||
SET last_text = NEW.text, last_sender_id = NEW.sender_id
|
||||
WHERE account_id = NEW.account_id
|
||||
AND chat_id = NEW.chat_id
|
||||
AND last_message_id = NEW.message_id;
|
||||
END IF;
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$
|
||||
"""
|
||||
|
||||
_NEWER = """
|
||||
CREATE FUNCTION chat_stats_newer(current chat_stats, incoming chat_stats)
|
||||
RETURNS boolean LANGUAGE sql IMMUTABLE AS $$
|
||||
SELECT incoming.last_date IS NOT NULL
|
||||
AND (current.last_date IS NULL
|
||||
OR (incoming.last_date, incoming.last_message_id)
|
||||
> (current.last_date, current.last_message_id))
|
||||
$$
|
||||
"""
|
||||
|
||||
_BACKFILL = """
|
||||
INSERT INTO chat_stats (account_id, chat_id, message_count, last_date,
|
||||
last_message_id, last_text, last_sender_id)
|
||||
SELECT agg.account_id, agg.chat_id, agg.message_count,
|
||||
last.date, last.message_id, last.text, last.sender_id
|
||||
FROM (
|
||||
SELECT account_id, chat_id, count(*) AS message_count
|
||||
FROM messages GROUP BY account_id, chat_id
|
||||
) agg
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT date, message_id, text, sender_id FROM messages m
|
||||
WHERE m.account_id = agg.account_id AND m.chat_id = agg.chat_id
|
||||
AND m.date <= now() + interval '1 day'
|
||||
ORDER BY m.date DESC, m.message_id DESC LIMIT 1
|
||||
) last ON true
|
||||
"""
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.execute(
|
||||
"CREATE TABLE chat_stats ("
|
||||
"account_id integer NOT NULL, "
|
||||
"chat_id bigint NOT NULL, "
|
||||
"message_count bigint NOT NULL DEFAULT 0, "
|
||||
"last_date timestamptz, "
|
||||
"last_message_id bigint, "
|
||||
"last_text text, "
|
||||
"last_sender_id bigint, "
|
||||
"PRIMARY KEY (account_id, chat_id))"
|
||||
)
|
||||
op.execute(_BACKFILL)
|
||||
op.execute(
|
||||
"CREATE INDEX ix_chat_stats_recent ON chat_stats "
|
||||
"(account_id, last_date DESC, chat_id DESC)"
|
||||
)
|
||||
op.execute(_NEWER)
|
||||
op.execute(_APPLY)
|
||||
op.execute(
|
||||
"CREATE TRIGGER messages_chat_stats_insert AFTER INSERT ON messages "
|
||||
"FOR EACH ROW EXECUTE FUNCTION chat_stats_apply()"
|
||||
)
|
||||
op.execute(
|
||||
"CREATE TRIGGER messages_chat_stats_update AFTER UPDATE ON messages "
|
||||
"FOR EACH ROW WHEN (OLD.text IS DISTINCT FROM NEW.text "
|
||||
"OR OLD.sender_id IS DISTINCT FROM NEW.sender_id) "
|
||||
"EXECUTE FUNCTION chat_stats_apply()"
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.execute("DROP TRIGGER IF EXISTS messages_chat_stats_update ON messages")
|
||||
op.execute("DROP TRIGGER IF EXISTS messages_chat_stats_insert ON messages")
|
||||
op.execute("DROP FUNCTION IF EXISTS chat_stats_apply()")
|
||||
op.execute("DROP FUNCTION IF EXISTS chat_stats_newer(chat_stats, chat_stats)")
|
||||
op.execute("DROP TABLE IF EXISTS chat_stats")
|
||||
Reference in New Issue
Block a user