-- IM group-message monthly sharding, baseline month: 2026-09 (UTC). -- Run in each tenant database. The application pre-creates subsequent months. CREATE TABLE IF NOT EXISTS im_group_message_shards ( shard_month CHAR(6) NOT NULL, table_name VARCHAR(64) NOT NULL, shard_start DATETIME(3) NOT NULL, shard_end DATETIME(3) NOT NULL, status VARCHAR(16) NOT NULL DEFAULT 'prepared', row_count BIGINT NOT NULL DEFAULT 0, min_created_at DATETIME(3) NULL, max_created_at DATETIME(3) NULL, created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), PRIMARY KEY (shard_month), UNIQUE KEY uk_im_group_message_shards_table (table_name), KEY idx_im_group_message_shards_status (status, shard_month) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; DROP TABLE IF EXISTS im_group_message_topic_shards; DROP TABLE IF EXISTS im_group_message_dedup; CREATE TABLE IF NOT EXISTS im_group_messages_202609 ( message_id BIGINT NOT NULL, topic VARCHAR(64) NOT NULL, group_id BIGINT NOT NULL, seqid BIGINT NOT NULL, from_user BIGINT NOT NULL, client_msg_id VARCHAR(64) NULL, msg_type VARCHAR(32) NOT NULL DEFAULT 'text', status VARCHAR(32) NOT NULL DEFAULT 'normal', content JSON NULL, head JSON NULL, mentioned_user_ids JSON NULL, mention_all TINYINT(1) NOT NULL DEFAULT 0, reply_seqid BIGINT NULL, root_seqid BIGINT NULL, file_count INT NOT NULL DEFAULT 0, content_size INT NOT NULL DEFAULT 0, has_risk TINYINT(1) NOT NULL DEFAULT 0, audit_status VARCHAR(32) NOT NULL DEFAULT 'pass', version INT NOT NULL DEFAULT 1, recalled_at DATETIME(3) NULL, recalled_by BIGINT NULL, deleted_at DATETIME(3) NULL, expires_at DATETIME(3) NULL, created_at DATETIME(3) NOT NULL, updated_at DATETIME(3) NOT NULL, PRIMARY KEY (message_id), UNIQUE KEY uk_202609_group_seq (group_id, seqid), UNIQUE KEY uk_202609_topic_seq (topic, seqid), UNIQUE KEY uk_202609_sender_client (topic, from_user, client_msg_id), KEY idx_202609_group_status_seq (group_id, status, seqid), KEY idx_202609_topic_created (topic, created_at), KEY idx_202609_topic_from_seq (topic, from_user, seqid), KEY idx_202609_audit_created (audit_status, created_at), KEY idx_202609_expires (expires_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; CREATE TABLE IF NOT EXISTS im_group_messages_202608 LIKE im_group_messages_202609; CREATE TABLE IF NOT EXISTS im_group_messages_202610 LIKE im_group_messages_202609; CREATE TABLE IF NOT EXISTS im_group_messages_202611 LIKE im_group_messages_202609; INSERT INTO im_group_message_shards (shard_month,table_name,shard_start,shard_end,status) VALUES ('202608','im_group_messages_202608','2026-08-01 00:00:00.000','2026-09-01 00:00:00.000','sealing'), ('202609','im_group_messages_202609','2026-09-01 00:00:00.000','2026-10-01 00:00:00.000','writable'), ('202610','im_group_messages_202610','2026-10-01 00:00:00.000','2026-11-01 00:00:00.000','prepared'), ('202611','im_group_messages_202611','2026-11-01 00:00:00.000','2026-12-01 00:00:00.000','prepared') ON DUPLICATE KEY UPDATE table_name=VALUES(table_name), shard_start=VALUES(shard_start), shard_end=VALUES(shard_end), status=IF(status IN ('sealed','archived'),status,VALUES(status)); -- Historical migration is deliberately separate from schema deployment. -- Backfill im_group_messages into the matching month table in bounded batches, -- then warm Redis topic routes before removing legacy reads.